Skip to main content
  1. Blog/

Building a Blog with Hugo & Blowfish

Introduction
#

This post documents how this site was built: using Hugo as the static site generator, paired with the Blowfish theme, hosted on GitHub Pages, and deployed automatically via GitHub Actions on every push.

The tech stack:

  • Hugo: A Go-based static site generator known for its fast builds.
  • Blowfish: A feature-rich, highly customizable Hugo theme.
  • GitHub Pages: Free static site hosting.
  • GitHub Actions: CI that builds and deploys the site automatically.

Prerequisites
#

Install Go
#

Hugo is built with Go. If you install Hugo via go install, you need a working Go environment first. Download it from the Go website and verify:

go version

Install Hugo
#

With Go in place, install a specific version of Hugo via go install:

go install github.com/gohugoio/hugo@v0.151.0

Note: The Blowfish theme requires the extended version of Hugo (for SCSS compilation). If you install via a package manager, make sure it is the extended build.

Verify the installation:

hugo version

Create the Site
#

Create a new Hugo site in your working directory:

hugo new site muzimu
cd muzimu
git init

Install the Blowfish Theme
#

Blowfish recommends adding the theme as a Git submodule for easy upgrades.

Add the Theme
#

git submodule add -b main https://github.com/nunocoracao/blowfish.git themes/blowfish

This creates a .gitmodules file in the project root:

[submodule "themes/blowfish"]
  path = themes/blowfish
  url = git@github.com:nunocoracao/blowfish.git
  branch = main

Update the Theme
#

To upgrade the theme later:

git submodule update --remote --merge

Initialize Submodules After Cloning
#

After cloning the repo on a new machine, sync and fetch the submodule:

git submodule sync
git submodule update --init --remote

Configure the Site
#

Blowfish keeps its configuration under config/_default/, split into several files by responsibility rather than a single hugo.toml. The key files:

FilePurpose
hugo.tomlCore settings (theme, baseURL, language, taxonomies)
params.tomlTheme parameters (appearance, layout, feature toggles)
languages.zh-cn.tomlChinese language and author info
languages.en.tomlEnglish language config
menus.zh-cn.tomlChinese navigation menu
markup.tomlMarkdown rendering and code highlighting

Basic Configuration
#

Set the theme and site URL in hugo.toml:

theme = "blowfish"
baseURL = "https://muzimu.github.io/"
defaultContentLanguage = "zh-cn"

enableRobotsTXT = true
enableEmoji = true

[taxonomies]
  tag = "tags"
  category = "categories"
  author = "authors"
  series = "series"

Author Info
#

Configure the author avatar, name, and social links in languages.zh-cn.toml:

[params.author]
  name = "muzimu"
  image = "img/avatar.jpg"
  headline = "muzimu"
  links = [
    { github = "https://github.com/muzimu" },
    { bilibili = "https://space.bilibili.com/448045568" },
  ]

For more theme parameters, see the official Blowfish docs: https://blowfish.page/docs/configuration/#theme-parameters

Create a Post
#

Use hugo new to create a post from an archetype template. This site uses the Page Bundle layout, where each post lives in its own directory for easier asset management:

hugo new content content/posts/Hugo/index.md

Example Front Matter (YAML):

---
title: "Post Title"
summary: "Post summary"
isCJKLanguage: true
date: 2025-09-06T17:42:06+08:00
tags:
  - tag
---

Setting isCJKLanguage: true lets Hugo correctly count words and reading time for CJK content.

Local Preview
#

Start the local dev server, available at http://localhost:1313/ by default:

hugo server

If a post is still a draft (draft: true), add -D to render it:

hugo server -D

Deploy to GitHub Pages
#

Prepare the Repositories
#

You need two repositories:

  • Source repo: Holds the Hugo project source (e.g. muzimu/muzimu).
  • Publish repo: Holds the build output, i.e. the GitHub Pages site (e.g. muzimu/muzimu.github.io).

Configure Automated Deployment
#

Create .github/workflows/hugo.yaml in the source repo. On every push to main, GitHub Actions builds the site and publishes the public/ directory to the target repo:

name: github pages

on:
  push:
    branches:
      - main

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
        with:
          submodules: true       # Fetch the Blowfish theme submodule
          fetch-depth: 0         # Full history for .GitInfo / .Lastmod

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

      - name: Build
        run: hugo --minify       # Build and minify static assets

      - name: Deploy
        uses: peaceiris/actions-gh-pages@v3
        with:
          external_repository: muzimu/muzimu.github.io
          personal_token: ${{ secrets.PERSONAL_TOKEN }}
          publish_dir: ./public
          publish_branch: main

Configure the Access Token
#

Because you publish to a different repository, the default GITHUB_TOKEN won’t work. Instead:

  1. Generate a token with repo scope on the GitHub Personal Access Tokens page.
  2. In the source repo, go to Settings → Secrets and variables → Actions and add a secret named PERSONAL_TOKEN with the token value.

Security note: A token is equivalent to your password. Never commit it in plain text inside the workflow file—GitHub will auto-revoke it and it risks leaking. Always reference it through Secrets.

Enable Pages and Visit
#

After pushing, wait for the Actions build to finish, confirm the publishing branch under the publish repo’s Settings → Pages, then visit your site at https://<username>.github.io/.

Summary
#

That’s it—a personal blog built on Hugo + Blowfish. Day to day, just create a post, preview it locally, and git push to publish automatically. Clean and efficient.