How to set up GitHub Actions for Hugo deployments
Automating Hugo builds via GitHub CI eliminates manual deployment friction and ensures production consistency. This guide provides exact YAML configurations, cache optimization strategies, and environment variable handling for reliable Static Site Generators in Production workflows.
Implementing deterministic pipelines guarantees zero-downtime releases. Follow the configuration steps below to establish a robust deployment architecture.
Workflow Initialization & Trigger Setup
Define repository events that initiate the build pipeline. Use push to main and pull_request triggers to validate changes before merging. Restrict concurrency to prevent race conditions during simultaneous commits.
Set workflow permissions explicitly to contents: read and pages: write for secure execution. The following YAML establishes the foundational job structure.
name: Deploy Hugo Site
on:
push:
branches: [main]
pull_request:
branches: [main]
permissions:
contents: read
pages: write
id-token: write
concurrency:
group: pages
cancel-in-progress: true
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- uses: peaceiris/actions-hugo@v3
with:
hugo-version: '0.120.0'
extended: true
- run: hugo --minify --gc
- uses: actions/upload-pages-artifact@v3
with:
path: ./public
Dependency Caching & Build Optimization
Reduce pipeline execution time by persisting module and theme caches. Cache the ~/.cache/hugo_cache directory and Go build artifacts across runs. Pin the Hugo extended version via actions/setup-hugo to avoid unexpected breaking changes.
Validate go.mod checksums before initiating the build process. Explicit cache declarations prevent redundant network fetches. For advanced matrix testing strategies, review GitHub Actions for Automated SSG Builds.
- name: Cache Hugo resources
uses: actions/cache@v4
with:
path: |
~/.cache/hugo_cache
~/.cache/go-build
key: ${{ runner.os }}-hugo-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-hugo-
Environment Variables & Secret Management
Securely inject API keys, base URLs, and draft toggles into the build context. Map HUGO_ENV=production to trigger asset minification and template optimizations. Store sensitive tokens in GitHub Repository Settings under Secrets.
Reference these values using ${{ secrets.TOKEN }} syntax within the env block. Never hardcode credentials directly into workflow files. Isolate environment-specific configurations using conditional step execution.
Deployment Step & Artifact Publishing
Transfer the generated /public directory to your hosting infrastructure. Upload the output as a workflow artifact for downstream processing. Utilize peaceiris/actions-gh-pages or the native GitHub Pages deployment action for seamless publishing.
Verify CNAME records and custom domain routing to prevent broken links. Ensure DNS propagation completes before validating the live endpoint. Configure branch protection rules to enforce successful pipeline runs prior to merging.
Common Pitfalls & Diagnostics
- Error: failed to load modules: Missing
HUGO_MODULE_WORKSPACEor incorrectgo.sumstate. Runhugo mod tidylocally and commit the updatedgo.sumto the repository root. - 404 Not Found on deployed assets: Incorrect
baseURLinconfig.tomlor missing trailing slash. EnsurebaseURLmatches the exact deployment domain and protocol. - Workflow stuck on Building sites... timeout: Uncached dependencies or infinite redirect loops in the theme. Enable debug logging with
--logLevel debugand verifytimeout-minutesin the workflow YAML.
Frequently Asked Questions
How do I deploy Hugo drafts to a preview environment?
Add HUGO_DRAFTS=true to the workflow env block and trigger on pull_request events to generate isolated preview builds.
Why does my GitHub Actions build fail with exec format error?
The runner architecture mismatches the Hugo binary. Explicitly specify extended: true and version: latest in actions/setup-hugo.
Can I cache Hugo builds across different branches?
Yes, by setting restore-keys in the cache action and using branch-specific cache keys like hugo-${{ runner.os }}-${{ github.ref_name }}.