Skip to content

CI/CD Pipelines for Static Sites — Automation & Deployment Guide

DodaTech Updated 2026-06-22 7 min read

In this tutorial, you'll learn about CI/CD Pipelines for Static Sites. We cover key concepts, practical examples, and best practices.

CI/CD pipelines automate building, testing, and deploying static sites on every code change — eliminating manual deployments, catching errors early, and enabling preview environments for every pull request before reaching production.

What You'll Learn

Why It Matters

Manual deployments are error-prone and time-consuming. A well-configured CI/CD pipeline builds your site, runs validation checks, deploys to a staging environment, and promotes to production — all triggered by a single git push. This reduces deployment time from minutes to seconds and ensures every change is tested before reaching users.

Real-World Use

A documentation team deploys preview sites for every pull request, letting reviewers verify changes before merging. A marketing team runs Lighthouse performance tests in CI and blocks deploys that drop below a score threshold. An open-source project automatically deploys the latest documentation whenever a new release tag is pushed.

CI/CD Pipeline Architecture

flowchart LR
  A[Git Push] --> B[CI Trigger]
  B --> C{Build Stage}
  C --> D[Hugo Build]
  D --> E[Test Stage]
  E --> F{Check Type}
  F --> G[HTML Validation]
  F --> H[Broken Links]
  F --> I[Lighthouse CI]
  G --> J[Deploy Stage]
  H --> J
  I --> J
  J --> K{Environment}
  K -->|PR| L[Preview Deploy]
  K -->|Main| M[Staging Deploy]
  K -->|Tag| N[Production Deploy]
  style A fill:#f90,color:#fff
  style B fill:#09f,color:#fff

CI/CD Platform Comparison

Feature GitHub Actions GitLab CI Cloudflare Pages Netlify
Free minutes 2,000/mo 400/mo Unlimited builds 300 builds/mo
Concurrent jobs 20 Unlimited (self-hosted) Unlimited 1
Cache support Yes (actions/cache) Yes Built-in Built-in
Preview deploys Manual setup Manual setup Automatic Automatic
Secrets management Encrypted secrets Masked variables Encrypted variables Encrypted
Matrix builds Yes Yes Limited No
Deploy to multiple Custom script Custom script Built-in Built-in

GitHub Actions Pipeline

Basic Build and Deploy Workflow

# .github/workflows/deploy.yml
name: Build and Deploy Hugo Site

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          submodules: true
          fetch-depth: 0

      - name: Setup Hugo
        uses: peaceiris/actions-hugo@v3
        with:
          hugo-version: '0.128.0'
          extended: true

      - name: Build
        run: hugo --gc --minify

      - name: Check for broken links
        run: |
          npx linkinator ./public --recurse --skip "^(?!https?://)"

      - name: Upload artifact
        uses: actions/upload-pages-artifact@v3
        with:
          path: ./public

Expected behavior: On every push to main or pull request, the workflow checks out the repository, installs Hugo 0.128.0 extended version, builds the site with garbage collection and minification, checks for broken links using linkinator, and uploads the public/ directory as a deploy artifact.

Deploy to Cloudflare Pages via API

# .github/workflows/deploy-cloudflare.yml
name: Deploy to Cloudflare Pages

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup Hugo
        uses: peaceiris/actions-hugo@v3
        with:
          hugo-version: '0.128.0'
          extended: true

      - name: Build site
        run: hugo --gc --minify

      - name: Deploy to Cloudflare Pages
        uses: cloudflare/pages-action@v1
        with:
          apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
          accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
          projectName: my-hugo-site
          directory: public
          gitHubToken: ${{ secrets.GITHUB_TOKEN }}

Expected behavior: After a successful Hugo build, the Cloudflare Pages action uploads the public/ directory to your Cloudflare Pages project. The apiToken and accountId are stored as GitHub secrets for security.

GitLab CI Pipeline

# .gitlab-ci.yml
image: registry.gitlab.com/pages/hugo/hugo:latest

variables:
  HUGO_ENV: production

before_script:
  - apk add --no-cache curl linkinator

pages:
  stage: deploy
  script:
    - hugo --gc --minify
    - linkinator ./public --recurse --skip "^(?!https?://)"
  artifacts:
    paths:
      - public
  rules:
    - if: $CI_COMMIT_BRANCH == "main"
      when: always
    - if: $CI_MERGE_REQUEST_IID
      when: always
  environment:
    name: production
    url: https://example.com

Testing and Validation

HTML Validation

- name: Validate HTML
  run: |
    npm install -g html-validate
    html-validate ./public/**/*.html

Lighthouse CI

- name: Lighthouse CI
  run: |
    npm install -g @lhci/cli
    lhci autorun --upload.target=temporary-public-storage

Expected behavior: Lighthouse CI runs performance, accessibility, SEO, and best-practice audits against the built site. Results are stored temporarily and linked in the CI output. You can configure score thresholds to fail the build if any score drops below a minimum.

- name: Check broken links
  run: npx linkinator ./public --recurse --skip "^(?!https?://)"

Environment Management

Environment Branch URL Indexing Purpose
Development Feature branches *.pages.dev No Developer testing
Staging staging staging.example.com No QA review
Production main example.com Yes Live site
Pull request PR source pr-123.pages.dev No Code review

Common Errors

1. CI Pipeline Fails with "command not found: hugo"

The CI runner does not have Hugo pre-installed. Always install Hugo explicitly using an action or script. For GitHub Actions, use peaceiris/actions-hugo. For GitLab CI, use a Hugo Docker image.

2. Build Times Exceeding CI Timeout

Large sites may exceed the default CI timeout (6 hours for GitHub Actions, but build minutes are limited). Optimize build times by caching Hugo's resources/_gen/ directory between runs:

- name: Cache Hugo resources
  uses: actions/cache@v4
  with:
    path: /tmp/hugo_cache
    key: ${{ runner.os }}-hugo-${{ hashFiles('**/go.mod') }}

3. Secrets Leaking in CI Logs

Never hardcode API tokens or secrets in workflow files. Use encrypted secrets in your CI platform settings and reference them as ${{ secrets.MY_SECRET }}. Mask secrets in log output.

4. Preview Deployments Not Updating on Subsequent Pushes

If a preview deployment URL remains unchanged but the content is stale, check that the CI pipeline is triggered on every push (including force pushes). Configure Git hooks or webhooks to rebuild when dependencies change.

5. Different Build Outputs Between Local and CI

CI environments may use different Hugo versions, operating systems, or environment variables than your local machine. Pin the Hugo version in CI with hugo-version: '0.128.0' and test builds locally with the same version.

Practice Questions

1. What is the purpose of fetch-depth: 0 in the GitHub Actions checkout step?

It fetches the full Git history, which is required for Hugo's enableGitInfo feature to display accurate "last modified" dates from Git commit timestamps.

2. How do you deploy to multiple environments from a single CI pipeline?

Use conditional rules based on branch names or tags. For example, push to main deploys to production, push to staging deploys to staging, and pull requests deploy to preview environments.

3. What is the benefit of caching Hugo's resources/_gen directory?

It avoids recompiling SCSS, reprocessing images, and reparsing templates on every build, reducing build times by 50-80% for subsequent runs.

4. Why should secrets be stored as CI encrypted variables rather than in the workflow file?

Workflow files are stored in the repository and visible to anyone with repository access. Encrypted variables are masked in logs and only accessible to authorized CI runs.

5. Challenge: Create a GitHub Actions workflow that builds a Hugo site, runs Lighthouse CI with a minimum Performance score of 90, checks for broken links, deploys to Cloudflare Pages staging on PR, and promotes to production only when a GitHub Release is created.

Mini Project: Complete CI/CD Pipeline for a Hugo Site

Build a production-grade CI/CD pipeline:

  1. Create a GitHub Actions workflow that builds on every push
  2. Add a caching step for Hugo resources to speed up builds
  3. Implement broken link checking and HTML validation as test steps
  4. Deploy preview environments for every pull request
  5. Deploy to production only from the main branch
  6. Add a CHANGELOG.md step that generates release notes from commit messages
  7. Configure Slack or email notifications for failed builds

Start with the workflow structure:

mkdir -p .github/workflows
touch .github/workflows/ci.yml

Test the pipeline by pushing to a feature branch, creating a pull request, and verifying the preview deployment appears with the correct content. Then merge to main and confirm the production deployment updates.

Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro