Step-by-step instructions to build and maintain a Hugo + PaperMod + AsciiDoc static site on GitHub Pages.
Prerequisites
Homebrew (macOS) or equivalent package manager
Git
Hugo Extended (installed via Homebrew)
brew install hugo
hugo versionVerify the output includes extended:
hugo v0.164.0+extended ...
Asciidoctor
Hugo requires asciidoctor to process .adoc files:
brew install asciidoctor
gem install asciidoctorVerify:
asciidoctor --version
Project Setup
Add the PaperMod Theme
git init
git submodule add --depth=1 https://github.com/adityatelange/hugo-PaperMod themes/PaperModConfigure hugo.toml
baseURL = "https://example.com/"
locale = "en"
title = "My Site"
theme = "PaperMod"
paginate = 10
enableRobotsTXT = true
enableEmoji = true
[params]
description = "Site description"
author = "Your Name"
defaultTheme = "auto"
ShowReadingTime = true
ShowToc = true
ShowCodeCopyButtons = true
[taxonomies]
tag = "tags"
category = "categories"
[menu]
main = [
{ identifier = "posts", name = "Posts", url = "/posts/", weight = 10 },
{ identifier = "about", name = "About", url = "/about/", weight = 20 },
]
[security.exec]
allow = ['^(dart-)?sass(-embedded)?$', '^go$', '^git$', '^node$', '^postcss$', '^tailwindcss$', '^asciidoctor$']
[markup.asciidocExt]
workingFolderCurrent = true
noHeaderOrFooter = true
safeMode = "unsafe"
attributes = { icons = "font", idseparator = "_", source-highlighter = "rouge", "rouge-style" = "monokai", experimental = true, sectanchors = true }Create Content Directory Structure
mkdir -p content/posts
mkdir -p static/images
mkdir -p content/archAdding Images
Images in Hugo are static files placed in the static/ directory. Only files inside static/ are copied to public/ during build.
For a Post with Images
mkdir -p static/images/my-post
cp /path/to/images/*.png static/images/my-post/Then in your AsciiDoc:

Hard-Linking Source Images
For content that is edited outside the project directory, use hard links so changes propagate automatically:
ln /path/to/source/image.png static/images/my-post/image.png| Hard links share the same inode. Editing the source file updates the site copy. Hard links cannot cross filesystem boundaries. |
Setting Up GitHub Actions
Create .github/workflows/hugo-deploy.yml:
name: "Deploy Hugo site to Pages"
on:
push:
branches:
- main
workflow_dispatch:
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: true
fetch-depth: 0
- uses: peaceiris/actions-hugo@v3
with:
hugo-version: "latest"
extended: true
- run: hugo --minify
- uses: actions/upload-pages-artifact@v3
with:
path: ./public
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/deploy-pages@v4The submodules: true checkout option is required for the PaperMod theme. |
Local Development
Development Server with Live Reload
hugo server -DThis starts a server at http://localhost:1313/. The -D flag includes draft content.
Hugo watches for file changes and automatically refreshes the browser.
Gotchas and Troubleshooting
Hugo Blocks Asciidoctor
When using AsciiDoc, Hugo’s security policy blocks external executables by default.
Add asciidoctor to [security.exec] allow in hugo.toml:
ERROR asciidoctor is not whitelisted in policy security.exec.allow
Fix: Add ^asciidoctor$ to the allow list.
Asciidoctor Not Found
Hugo: asciidoctor ... unable to locate
Install asciidoctor:
brew install asciidoctor
Theme Not Found
ERROR module "PaperMod" not found
Make sure the theme submodule is initialized:
git submodule update --init --recursive
Image 404 in Development
If images don’t load in the Hugo dev server, check:
1. Images are in static/ directory (not assets/)
2. The :imagesdir: in AsciiDoc matches the directory path
3. Image filenames are case-sensitive
Hugo serves static/ content at the root. An image at static/images/foo.png is served at /images/foo.png.
Bundler 4.x Gemfile Resolution (Jekyll migration only)
Bundler 4.x walks parent directories to find a Gemfile. If running bundle exec in a subdirectory and a parent has a different Gemfile, bundler may resolve the wrong one.
Workaround: Set BUNDLE_GEMFILE explicitly:
BUNDLE_GEMFILE=/path/to/project/Gemfile bundle exec jekyll build
Hugo Deprecation Warnings
Older themes may use deprecated Hugo APIs. Warnings like:
WARN deprecated: .Language.LanguageCode was deprecated
These are benign and come from the theme, not user code. They will be fixed in future theme releases.
Hard Links on macOS
Hard links cannot: - Link directories (use symlinks for directories) - Cross filesystem boundaries - Be tracked by Git (Git stores content, not links)
Hard links are useful for keeping source content in sync with the site build directory, but use symlinks or copies for directories.
