GitLab Pages Deployment Prompt
Deploy static sites via GitLab Pages — custom domains, SSL, multiple sites, environment routing, parallel deploys.
- Target user
- DevOps engineers and developers using GitLab Pages
- Difficulty
- Beginner
- Tools
- Claude, ChatGPT
The prompt
You are a senior DevOps engineer who has built static-site CI/CD with GitLab Pages — for docs sites, marketing pages, Hugo/Jekyll/Astro/Next-static builds. I will provide: - The static site framework (Hugo, Jekyll, Astro, Next.js static, plain HTML) - The custom domain situation (gitlab.io subdomain only, custom domain, with SSL) - Build complexity (Node modules, asset pipeline, sitemap) - Multi-site / multi-env requirements Your job: 1. **Verify the basics**: - Job named `pages` (special name; required) - Artifacts include `public/` directory (default Pages root) - Job runs on default branch (configurable) - Output served at `https://<user-or-group>.gitlab.io/<project>` (or custom domain) 2. **For framework-specific builds**: - **Hugo**: `hugo --baseURL https://$CI_PAGES_URL` → outputs to `public/` - **Jekyll**: `bundle exec jekyll build -d public` - **Astro**: `npm run build` → outputs to `dist/`; either rename to `public/` or use `--output public` - **Next.js static**: `next build && next export` (older) or `output: 'export'` (newer); copy to `public/` - **Plain HTML**: just put it in `public/` 3. **For custom domain**: - Settings → Pages → New Domain → enter your domain - DNS: `CNAME` record pointing to `<user-or-group>.gitlab.io` (or A record to GitLab Pages IP) - Verification: TXT record GitLab provides - Wait for verification + SSL provisioning (Let's Encrypt automatic) 4. **For multiple sites in one project**: - Subdirectories under `public/` are accessible - Or use `routes:` (GitLab Pages CI feature in newer versions) 5. **For preview / review pages** per MR: - Pages doesn't support per-MR previews natively - Use review apps with an external static host (Netlify, S3+CloudFront, k8s ingress) - Or use child pipelines to deploy to a separate "preview" Pages project 6. **For build performance**: - Cache `node_modules`, `~/.bundle`, `~/.cache/hugo` - Pre-built CI image with toolchain baked in 7. **For SEO**: - Set `$CI_PAGES_URL` (auto-set) or canonical URL - Add a `robots.txt` and sitemap - Verify with Search Console 8. **For limits**: - GitLab.com Pages: 1GB total per project (configurable for self-managed) - Build time: same as regular CI jobs Mark DESTRUCTIVE: removing the `pages` job mid-flight (existing site stays but no updates), changing default branch without updating pages job rules (site stops updating), serving private content via Pages (it's public by default). --- Framework: [Hugo / Jekyll / Astro / Next / plain] Custom domain?: [yes/no + DNS state] Build complexity: [DESCRIBE] Multi-site / multi-env: [DESCRIBE] Goal: [setup new / debug existing / optimize]
Why this prompt works
GitLab Pages setup is mostly mechanical but with framework-specific gotchas (output dir, baseURL, asset paths). This prompt covers the recipe per framework.
How to use it
- Use a published recipe per framework.
- Don’t forget the
public/output requirement. - Set the right baseURL so internal links work.
- For custom domain, verify DNS BEFORE adding in GitLab.
Recipes
Hugo
pages:
image: registry.gitlab.com/pages/hugo:latest
stage: deploy
script:
- hugo --minify --baseURL "$CI_PAGES_URL"
artifacts:
paths: [public]
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
Jekyll
pages:
image: ruby:3.3
stage: deploy
script:
- bundle install
- bundle exec jekyll build -d public --baseurl "$CI_PAGES_URL"
artifacts:
paths: [public]
cache:
key:
files: [Gemfile.lock]
paths:
- vendor/ruby
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
Astro
pages:
image: node:20
stage: deploy
script:
- npm ci
- npm run build
- mv dist public # Astro outputs to dist by default
artifacts:
paths: [public]
cache:
key:
files: [package-lock.json]
paths: [node_modules]
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
Next.js static export
pages:
image: node:20
stage: deploy
script:
- npm ci
- npm run build # next.config.js has output: 'export'
- mv out public
artifacts:
paths: [public]
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
// next.config.js
module.exports = {
output: 'export',
basePath: process.env.CI_PROJECT_NAME ? `/${process.env.CI_PROJECT_NAME}` : '',
};
Plain HTML
pages:
stage: deploy
script:
- mkdir -p public
- cp -r site/* public/
artifacts:
paths: [public]
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
Custom domain setup
- In GitLab: Settings → Pages → New Domain → enter your domain
- In DNS: Create:
CNAMEfrom your domain to<user-or-group>.gitlab.io(forwww.example.com)- For apex (
example.com): use ALIAS/ANAME record OR A record to GitLab Pages IPs (35.185.44.232on gitlab.com) TXTrecord GitLab provides for verification (e.g.,_gitlab-pages-verification-code.example.com TXT "code")
- Wait for verification (~1 min) + SSL provisioning (~10 min)
- Force HTTPS: Settings → Pages → toggle “Force HTTPS”
Multi-project preview pattern (review apps approximation)
# Parent pipeline: deploy main to Pages
pages:
stage: deploy
script: ./build.sh
artifacts: { paths: [public] }
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
# Child / separate project: deploy MR previews to S3 + CloudFront
preview:
stage: deploy
script:
- ./build.sh
- aws s3 sync public/ s3://previews/mr-$CI_MERGE_REQUEST_IID/
environment:
name: preview/$CI_COMMIT_REF_SLUG
url: https://previews.example.com/mr-$CI_MERGE_REQUEST_IID/
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
Cache strategy
.cache: &cache
cache:
key:
files:
- package-lock.json
paths:
- node_modules/
- ~/.cache/hugo # for Hugo
- vendor/ruby # for Jekyll
policy: pull-push
Common findings this catches
- 404 after deploy →
public/not in artifacts, or framework output dir not renamed. - Internal links broken →
baseURLnot set; should be$CI_PAGES_URL. - Site updates on push but not on first deploy → wait for initial build (~5 min from job complete).
- Custom domain stuck “Pending verification” → TXT record not propagated; verify with
dig TXT _gitlab-pages-.... - HTTPS shows “not secure” → Let’s Encrypt provisioning failed; check DNS resolution from public.
- Pages job runs but no deploy → Job named something other than
pages; required exact name. - Site > 1GB → trim assets (images, generated files), or split into multiple Pages projects.
When to escalate
- DNS issues persistent → coordinate with DNS team; verify NS records.
- Large Pages site exceeding self-managed quotas — admin coordination.
- Pages outage on gitlab.com — check https://status.gitlab.com/.
Related prompts
-
GitLab CI/CD Cache vs Artifacts Design Prompt
Choose between cache and artifacts in GitLab CI/CD — design cache keys that invalidate correctly, set artifact expiry, and avoid the common 'cache as artifact' mistake.
-
GitLab Environments & Deployments Debug Prompt
Diagnose GitLab environments — stuck deployments, environment scope, `stop_in` cleanup, protected environments, deployment tier confusion.
-
GitLab CI/CD Pipeline Optimization Prompt
Speed up slow GitLab pipelines — DAG with `needs:`, cache vs artifacts, parallel jobs, image pre-builds, dependency proxy, and shallow clones.