Skip to content
CloudOps
Newsletter
All prompts
AI for GitLab CI/CD Difficulty: Beginner ClaudeChatGPT

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

  1. Use a published recipe per framework.
  2. Don’t forget the public/ output requirement.
  3. Set the right baseURL so internal links work.
  4. 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

  1. In GitLab: Settings → Pages → New Domain → enter your domain
  2. In DNS: Create:
    • CNAME from your domain to <user-or-group>.gitlab.io (for www.example.com)
    • For apex (example.com): use ALIAS/ANAME record OR A record to GitLab Pages IPs (35.185.44.232 on gitlab.com)
    • TXT record GitLab provides for verification (e.g., _gitlab-pages-verification-code.example.com TXT "code")
  3. Wait for verification (~1 min) + SSL provisioning (~10 min)
  4. 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 deploypublic/ not in artifacts, or framework output dir not renamed.
  • Internal links brokenbaseURL not 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

Newsletter

Free: the DevOps AI Incident-Triage Cheat Sheet

Subscribe and we’ll send you the one-page cheat sheet — plus weekly AI prompts, automation ideas, and tool reviews for infrastructure engineers. One email a week. No spam, unsubscribe anytime.

  • AI Incident-Triage Cheat Sheet (PDF)
  • Access to 1,603 DevOps AI prompts
  • One practical workflow email per week