🚀Hugo: Deploy with GitHub Actions

Apr 14, 2021 23:00 · 834 words · 4 minute read hugo GitHub GitHub-Actions CI/CD GitHub-Pages

Setting up automated deployment for my Hugo blog was one of those tasks I kept putting off. You know how it is – the manual process works, so why change it? But once I finally took the plunge with GitHub Actions, I wondered why I waited so long.

This blog you’re reading right now is built with Hugo (a blazingly fast static site generator written in Go) and automatically deploys to GitHub Pages every time I commit a new post. Let me walk you through how I set it up.

How I Started (And Why It Became a Problem)

Initially, I had a pretty simple setup. I kept my blog content in a private repository and had another separate repository for the generated HTML/CSS files that GitHub Pages serves.

My workflow was straightforward but tedious:

  1. Write a post locally
  2. Run the Hugo CLI to generate the static files
  3. Manually commit and push the generated files

This worked fine when I was always writing from my main computer. But try editing a post from a different machine without Hugo installed? That’s when things got frustrating fast.

The Forestry Detour

Looking for a better solution, I moved to Forestry – a git-backed CMS for static websites. It was fantastic! Writing and publishing posts felt effortless, like having a WordPress admin panel for my static site.

When Things Changed

But last year, Forestry dropped deployment support for Hugo sites. Their reasoning made sense from a business perspective:

When it comes to automate your deployments and host your static website on a CDN, it’s the responsibility of a continuous deployment and hosting service. https://forestry.io/docs/hosting/.

Suddenly, I was back to manual deployments. That’s when I finally embraced GitHub Actions.

Enter GitHub Actions

GitHub Actions is GitHub’s built-in automation platform. Think of it as a way to tell GitHub: “Hey, whenever I push code to this repository, run these specific tasks automatically.”

For my blog, those tasks are:

  1. Take my Hugo source files
  2. Generate the static website
  3. Push the generated files to my GitHub Pages repository

The best part? It’s surprisingly beginner-friendly once you understand the basic structure.

The Solution: Automated Deployment

Here’s the exact GitHub Actions workflow file I use. Don’t worry if it looks intimidating – I’ll break it down after:

# Workflow to help you get started with Hugo deployment

name: Deploy

# Controls when the action will run. Triggers the workflow on push or pull request
# events but only for the master branch
on:
  push:
    branches: master

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
  # This workflow contains a single job called "deploy"
  deploy:
    # The type of runner that the job will run on
    runs-on: ubuntu-latest

    # Steps represent a sequence of tasks that will be executed as part of the job
    steps:
      # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
      - uses: actions/checkout@v2

      # Github Actions fo Hugo
      - name: Setup Hugo
        uses: peaceiris/actions-hugo@v2
        with:
          hugo-version: "0.81.0"

      # Creates ./public directory
      - name: Build
        run: hugo --minify

      # Upload/Commit the contents from publish_dir -> external_repository
      - name: Deploy
        uses: peaceiris/actions-gh-pages@v3
        with:
          external_repository: aneesahammed/aneesahammed.github.io
          publish_branch: master
          personal_token: ${{secrets.GH_TOKEN}}
          user_name: aneesahammed
          user_email: aneesahammed@mail.com
          publish_dir: ./public

Breaking It Down

Let me explain what each part does in plain English:

The Trigger: on: push: branches: master means “run this workflow whenever I push code to the master branch.”

The Job Steps:

  1. Checkout: GitHub grabs my repository files
  2. Setup Hugo: Installs Hugo version 0.81.0 on the virtual machine
  3. Build: Runs hugo --minify to generate my static site
  4. Deploy: Takes the generated files and pushes them to my GitHub Pages repository

Setting Up Your Personal Token

The trickiest part is the personal_token requirement. Since we’re pushing to an external repository (your GitHub Pages repo), you need to give GitHub Actions permission to do that.

Here’s how:

  1. Go to GitHub’s token settings
  2. Generate a new personal access token with repository permissions
  3. Add it to your blog repository’s secrets (Settings → Secrets → Actions)
  4. Name it GH_TOKEN

secrets tab from GitHub repo

Key Configuration Points

If you’re setting this up for your own blog, you’ll need to change these fields:

  • external_repository: Your GitHub Pages repository (usually username.github.io)
  • publish_branch: The branch your GitHub Pages site serves from (usually master or main)
  • user_name and user_email: Your GitHub credentials

For more detailed Hugo setup options, check out the Hugo Setup GitHub Action.

The Payoff

Now every time I commit a new post or make changes to my blog, GitHub automatically:

  • Builds the site
  • Deploys it to GitHub Pages
  • Shows me a nice green checkmark when it’s done

Deployment success

No more manual builds. No more remembering to push generated files. Just write, commit, and let GitHub handle the rest.

If you’re running a Hugo blog and still doing manual deployments, I can’t recommend this approach enough. The initial setup takes maybe 30 minutes, but it’ll save you hours down the road.

Got questions about setting this up? Feel free to reach out on Twitter

tweet Share