Building a Blog with Hugo + GitHub
Summary: A walkthrough of building a personal blog using the Notion → Markdown conversion → Hugo build → GitHub Pages deployment flow. Follow along with Hugo installation, applying the m10c theme, automated deployment with GitHub Actions, and baseURL configuration pitfalls to reduce deployment errors.

Introduction
I had been keeping technical notes in Evernote and personal documents, and was preparing to run a blog using Notion’s website feature.
However, Notion had limitations on customization, and using a custom domain came with extra costs, which gave me pause.
As an alternative, I considered switching to velog or rewriting everything in Markdown and moving to Jekyll.
But I couldn’t give up Notion, which is so convenient to write in. My conclusion: write in Notion and deploy it as a static website!
Goals
- Build documents written as md files with Hugo
- Automate deployment with GitHub Pages
💡 Build Environment
- Test environment: Mac
- Deployment environment: GitHub Actions
Why I Chose Hugo
- Has a large number of GitHub stars and is actively updated
- Faster than Jekyll when building over 1,000 pages
This blog currently runs on the following flow. (Source reference: )
Write in Notion
→ Convert to Markdown via the Notion API→ Build a static site with Hugo
→ Deploy to GitHub Pages
Preparation
Choosing a Hugo Theme
I first picked a theme from Hugo Themes.
Chosen theme: m10c
Theme selection criteria
- Supports SEO optimization features
- Supports multilingual site features
Some features aren’t fully supported in m10c, but this can be addressed with Hugo’s layout overrides.
Installing Hugo
Installation docs: Installation Guide
Hugo docs: Documentation
Mac example
1# Install Hugo
2brew install hugo
3
4# Verify installation
5hugo --version
Creating a Hugo Site
Initializing the Project
1# Create a working directory
2mkdir hugo && cd hugo
3
4# Create a Hugo site
5hugo new site .
6
7# Check the result
8tree
9# .
10# ├── archetypes
11# │ └── default.md
12# ├── assets
13# ├── content
14# ├── data
15# ├── hugo.toml
16# ├── i18n
17# ├── layouts
18# ├── static
19# └── themes
Installing the Theme
Install the theme using a Git submodule.
1# Initialize the Git repository (if needed)
2git init
3
4# Add the theme submodule
5git submodule add https://github.com/vaga/hugo-theme-m10c.git themes/m10c
6
7# Verify installation
8ls -al themes/m10c
Copying Sample Content (Optional)
1# Copy the theme's sample content
2cp -R themes/m10c/exampleSite/content ./content
3
4# Check
5ls -al ./content/
Hugo Configuration
Replace the default hugo.toml config file with the theme’s sample configuration.
1# Delete the existing configuration
2rm hugo.toml
3
4# Copy the sample configuration
5cp themes/m10c/exampleSite/config.toml ./hugo.toml
Open the hugo.toml file and edit the basic settings.
1baseURL = "https://testblog.plzhans.com"
2title = "Test blog"
3theme = "m10c"
Note: Remove the themesDir setting, and make sure theme matches the actual directory name.
Running the Local Server
1# Start the development server
2hugo server -D
Example output:
1Watching for changes in /Users/plzhans/temp/sample/hugo/...
2Start building sites …
3hugo v0.154.5+extended+withdeploy darwin/arm64 BuildDate=2026-01-11T20:53:23Z
4
5Built in 2 ms
6Environment: "development"
7Web Server is available at http://localhost:57264/
8Press Ctrl+C to stop
Open the address shown in your browser to check the result.
http://localhost:57264
Deploying to GitHub Pages
Creating a Repository
Create a new repository on GitHub.
Choosing a Deployment Strategy
Both Jekyll and Hugo manage source and build output separately.
Jekyll is automatically detected and deployed by GitHub Pages, but Hugo must be deployed directly through GitHub Actions.
When choosing a deployment strategy, pay attention to whether the source repository is public or private.
If you want the source repository to be private, keep the following in mind.
Free plan
- Only public repositories can enable Pages.
- So if you want to keep the source private, use Method 3 to keep the source repository private while making only the deployment repository public.
Paid plan
- Pages can be public even if the repository is private.
Method 1: actions/deploy-pages
- Uses 1 repository
- Set the GitHub Pages source to GitHub Actions
- Push to the main branch → Hugo build → upload artifact → automatic deployment
Method 2: peaceiris/actions-gh-pages
- Uses 1 repository
- Connect GitHub Pages to the gh-pages branch
- Push to the main branch → Hugo build → commit to the gh-pages branch
Method 3: Separate Deployment Repository
- Uses 2 repositories (source repository, deployment repository)
- Push the build output to the deployment repository
Method 4: Uploading Build Output Elsewhere
- You don’t have to use GitHub Pages.
- You can simply upload the build output to a directory connected to a web server.
- By default, the output is generated in the
/publicdirectory.
This document uses Method 1 to establish the deployment strategy.
Configuring GitHub Pages
Repository → Settings → Pages → set Source to GitHub Actions
Writing the GitHub Actions Workflow
Create a .github/workflows/deploy-hugo.yml file.
1name: Deploy Hugo
2
3on:
4 push:
5 branches: [ master ]
6
7permissions:
8 contents: read
9 pages: write
10 id-token: write
11
12concurrency:
13 group: pages
14 cancel-in-progress: true
15
16env:
17 HUGO_BASEURL: https://plzhans.github.io/hugo-sample/
18
19jobs:
20 build-and-deploy:
21 runs-on: ubuntu-latest
22 env:
23 HUGO_CACHEDIR: /tmp/hugo_cache
24
25 steps:
26 - name: Checkout
27 uses: actions/checkout@v4
28 with:
29 submodules: recursive
30 fetch-depth: 1
31
32 - name: Setup Hugo
33 uses: peaceiris/actions-hugo@v3
34 with:
35 hugo-version: "latest"
36 extended: true
37
38 - name: Cache Hugo
39 uses: actions/cache@v4
40 with:
41 path: $ env.HUGO_CACHEDIR
42 key: $ runner.os -hugomod-$ hashFiles('**/go.sum')
43 restore-keys: |
44 $ runner.os -hugomod-
45
46 - name: Build
47 run: hugo --minify --gc --cleanDestinationDir --baseURL "$HUGO_BASEURL"
48
49 - uses: actions/upload-pages-artifact@v3
50 with:
51 path: ./public
52
53 - uses: actions/deploy-pages@v4
Deploying with Git
1# Add the remote repository
2git remote add origin [email protected]:plzhans/hugo-sample.git
3
4# Set up .gitignore
5echo "/public/" >> .gitignore
6
7# Commit all files
8git add .
9git commit -m "first commit"
10
11# Create the branch and push
12git branch -M master
13git push -u origin master
Verifying the Deployment
Check the workflow run under the GitHub Actions tab, and check the deployed URL under Settings → Pages.
Example address: https://plzhans.github.io/hugo-sample/
Notes
baseURL configuration
If the baseURL in hugo.toml or the --baseURL option at build time is incorrect, the CSS and image paths will be wrong and errors will occur.
In this guide, the deployment address is set via the HUGO_BASEURL environment variable in the GitHub Actions workflow.
Related Posts
- Overview: Building a Hugo Blog - From Start to SEO
- Custom domain setup: Using a Custom Domain with GitHub Pages
- Multilingual (i18n) support setup: Setting Up Multilingual Support for a Hugo Site
- (Coming soon) Automating the deployment of Notion-written posts to GitHub Pages