GitLab Pages From CI: Ship Docs, Coverage Reports and Static Sites for Free
Use GitLab Pages and CI to publish documentation, coverage reports and static sites — with per-MR previews, custom domains and HTTPS — straight from your pipeline.
- #gitlab
- #cicd
- #gitlab-pages
- #static-sites
- #documentation
- #ssg
Most teams have a pile of static content they never publish well: API docs that live in a wiki nobody updates, coverage reports buried in CI artifacts, a generated changelog that only exists in a release notes field. GitLab Pages turns any of that into a real, HTTPS-served website built straight from your pipeline — and it’s been sitting in your GitLab instance the whole time.
This is how to use it for the things that actually pay off, not just a hello-world landing page.
The one rule that trips everyone up
GitLab Pages has exactly one hard requirement that everybody gets wrong the first time: the job must be named pages, and it must publish to a directory called public. Not pages-deploy, not dist, not build. The job is pages, the output is public. GitLab keys off both.
pages:
stage: deploy
script:
- npm ci
- npm run build
- mv dist public # whatever your SSG outputs → public
artifacts:
paths:
- public
rules:
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
That’s a complete, working Pages deploy. The public directory becomes an artifact, GitLab picks it up, and your site is live at https://<group>.gitlab.io/<project> within a minute or two. Restricting it to the default branch keeps feature branches from clobbering your published site.
Publish your docs site
The highest-value use is documentation generated by a static site generator — MkDocs, Docusaurus, Astro, Hugo, whatever. The pattern is identical regardless of tool: build, move output to public, artifact it. For a Hugo example:
pages:
stage: deploy
image: hugomods/hugo:latest
script:
- hugo --minify --destination public
artifacts:
paths: [public]
rules:
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
Now your docs rebuild and republish on every merge to main, automatically. No separate hosting, no manual upload step, no docs that drift three versions behind the code. The docs live in the same repo as the code and ship with it.
Surface your coverage and test reports
Here’s a use most teams miss: publish your HTML coverage report and test output as a browsable site. Instead of downloading a CI artifact and opening index.html locally, you give everyone a permanent URL:
pages:
stage: deploy
script:
- npm ci
- npm run test:coverage # outputs coverage/ as HTML
- mkdir public
- cp -r coverage/* public/
artifacts:
paths: [public]
rules:
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
A bookmarkable coverage dashboard gets looked at; a CI artifact does not. Same content, completely different adoption.
Per-merge-request previews with Pages
GitLab Pages now supports parallel deployments, which means you can publish a preview for each merge request — a static analogue of review apps. You give each MR its own path prefix:
pages:
stage: deploy
script:
- npm run build
- mv dist public
pages:
path_prefix: "$CI_COMMIT_REF_SLUG"
artifacts:
paths: [public]
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
With path_prefix set to the branch slug, each MR’s build lives at its own URL. A reviewer clicks through the actual rendered docs for the change instead of imagining what the markdown will look like. For dynamic, server-backed previews you’d reach for full review apps — covered in our GitLab CI/CD guides — but for static content, Pages previews are far cheaper.
Custom domains and HTTPS
Pages gives you a *.gitlab.io URL for free, but you can point a custom domain at it with automatic Let’s Encrypt certificates. Add the domain under Settings → Pages, create the DNS record GitLab tells you to, and toggle on the automatic certificate. You get docs.yourcompany.com over HTTPS with zero certificate management. Combined with Settings → General → Visibility, you can also keep a Pages site private to project members — handy for internal docs you don’t want public.
Where AI earns its place
Static site generators have a frustrating failure mode: the build passes locally and fails in CI, usually over a base-URL or path-prefix mismatch (links work locally, 404 in production because the site is served from a subpath). Paste the build config and the broken-links symptom into an AI assistant and ask “why are my asset paths wrong when served from a subdirectory?” — it’s a five-minute fix instead of an afternoon of trial and error.
For the pipeline config itself, our AI Code Review assistant catches the classic Pages mistakes — wrong job name, wrong output directory, missing artifact path — before they cost you a confused debugging session.
The takeaway
GitLab Pages is the most underused feature in the CI suite. Job named pages, output to public, artifact it — and suddenly your docs, coverage reports, and changelogs are real websites that rebuild on every merge. Add per-MR previews and a custom domain and you’ve got a publishing pipeline most teams pay a separate vendor for, sitting inside the GitLab you already run.
Build configuration advice and AI-generated fixes are assistive. Verify your Pages deploy and links against the live published site before relying on it.
Download the Free 500-Prompt DevOps AI Toolkit
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.