Alright, let’s get your Hugo site onto GitHub Pages. This is where we stop treating our repository like a fancy file cabinet and start making it do some real work. The magic wand here is GitHub Actions, which is essentially a robot butler you can instruct with a YAML file. We’re going to write a set of commands that will tell GitHub, “Hey, every time I push new content to the main branch, build the site for me and shove the results into the gh-pages branch.” It’s automation, and it’s glorious.

The Core Workflow File

You’ll create a file in your repository at .github/workflows/hugo.yaml. The .github/workflows directory is where GitHub automatically looks for these automation scripts. The name hugo.yaml can be anything, but make it descriptive.

Here’s the blueprint for our robot butler. This isn’t just a snippet; this is the complete, runnable incantation.

name: Deploy Hugo site to Pages

on:
  push:
    branches: [main]

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

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

      - name: Build
        run: hugo --minify

      - name: Deploy
        uses: peaceiris/actions-gh-pages@v3
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./public

Let’s break down why each part is there, because cargo-culting YAML is a one-way ticket to frustration city.

Why the submodules and fetch-depth Options Matter

If you’re using a theme pulled in as a Git submodule (which is how many Hugo themes are installed), the submodules: recursive option is non-negotiable. It tells the checkout action to not only get your code but also to go out and fetch the theme’s code too. Without it, your build will fail with a cryptic error about missing themes. The fetch-depth: 0 pulls the entire history, which is necessary for Hugo to be able to calculate the “lastmod” date for your pages. If you omit it, all your pages will show the same “last modified” date, which is the time of the build. Lame.

The “Extended” Hugo Gotcha

Hugo has two builds: the standard and the extended version. The extended version is required if you’re processing CSS with tools like PostCSS or Sass. Most modern themes need it. If you don’t specify extended: true and your theme requires it, the build will run but your CSS will be utterly broken. The site will look like a time traveler from 1994. It’s the most common “it built successfully but my site looks awful” pitfall. The action we’re using (peaceiris/actions-hugo) is smart enough to fetch the correct extended version for you.

The Almighty GITHUB_TOKEN

You’ll notice we use secrets.GITHUB_TOKEN. This is a special secret token that GitHub automatically creates for every run. You don’t have to set this up yourself. It’s the butler’s key to the house. It grants this specific workflow run just enough permissions to push the built files from the public directory to the gh-pages branch. It’s beautifully secure and hands-off.

Configuring Your Repository for Pages

The workflow does the building, but GitHub needs to know where to find the built files. Go to your repository’s Settings > Pages. Under Source, select GitHub Actions. Yes, this is a new and much better way to do it than the old “select a branch” method. Once your workflow runs successfully for the first time, it will create the gh-pages branch and GitHub will automatically start using it. If you had an old gh-pages branch from the legacy method, I recommend deleting it to avoid confusion. Let the robot handle it.

Debugging When It Goes Wrong

The workflow ran, but it failed. Now what? Click on the “Actions” tab in your repo. Click on the failed run. The log output is your best friend. It will tell you exactly which step exploded.

  • Build step fails: Usually a problem with your Hugo site itself. Run hugo server locally. Does it work? The CI isn’t a magician; it can’t build a broken site.
  • “Error: failed to extract module:” You forgot submodules: recursive.
  • “No such file or directory” in the theme: Your theme wasn’t fetched. See above.
  • Deployment fails with permissions errors: You’re probably trying to use a custom secret instead of GITHUB_TOKEN. Don’t. Use the provided token.

This workflow is a workhorse. It’s simple, reliable, and leverages the platform perfectly. Set it up once, and you can forget about it and get back to writing content. That’s the whole point.