Why Hugo
The previous blog used Next.js + Docker — powerful but overkill for a personal blog. Every build required pulling Node.js dependencies, compiling TypeScript, and bundling React. Deployment needed a running Node process.
Hugo is a static site generator written in Go, with clear advantages:
- Blazing fast builds: entire site builds in ~160ms
- Zero runtime dependencies: generates pure static files, no Node.js needed
- Simple deployment: just copy HTML files to a web server
- Single binary: one
hugo command does everything
Step 1: Install Hugo
1
2
3
4
5
6
| # Ubuntu/Debian - install extended version (supports SCSS)
wget -q https://github.com/gohugoio/hugo/releases/download/v0.147.5/hugo_extended_0.147.5_linux-amd64.deb
sudo dpkg -i hugo_extended_0.147.5_linux-amd64.deb
# Verify
hugo version
|
Note: Modern themes like PaperMod require Hugo v0.146.0+. Make sure to install a recent version. The extended edition is needed for SCSS compilation.
Step 2: Create a Hugo Site
1
2
| hugo new site blog
cd blog
|
Hugo generates this structure:
1
2
3
4
5
6
7
8
9
10
| blog/
├── archetypes/ # Content templates
├── assets/ # Processable assets (SCSS, etc.)
├── content/ # Blog posts
├── data/ # Data files
├── i18n/ # Translation files
├── layouts/ # Custom layout templates
├── static/ # Static assets (copied as-is)
├── themes/ # Theme directory
└── hugo.toml # Site configuration
|
Step 3: Install a Theme
Choose PaperMod — one of the most popular Hugo blog themes: clean, fast, and feature-rich.
1
2
3
4
5
| # Initialize git
git init
# Install theme as submodule
git submodule add --depth=1 https://github.com/adityatelange/hugo-PaperMod.git themes/PaperMod
|
Using git submodule makes theme updates easy:
1
| git submodule update --remote --merge
|
Edit hugo.toml:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
| baseURL = 'http://blog.quickpay.wang:8026/'
languageCode = 'zh-cn'
defaultContentLanguage = 'zh'
title = 'My Blog'
theme = 'PaperMod'
[pagination]
pagerSize = 10
enableRobotsTXT = true
buildDrafts = false
[params]
env = "production"
description = "Personal blog - Tech notes and life records"
author = "admin"
defaultTheme = "auto"
ShowReadingTime = true
ShowShareButtons = true
ShowPostNavLinks = true
ShowBreadCrumbs = true
ShowCodeCopyButtons = true
ShowWordCount = true
UseHugoToc = true
[markup]
[markup.highlight]
codeFences = true
guessSyntax = true
lineNos = true
style = "monokai"
[markup.goldmark]
[markup.goldmark.renderer]
unsafe = true
|
Multilingual Setup
Hugo has native multilingual support:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| [languages]
[languages.zh]
title = '我的博客'
languageName = '中文'
weight = 1
[[languages.zh.menu.main]]
identifier = 'home'
name = '首页'
url = '/'
[languages.en]
title = 'My Blog'
languageName = 'English'
weight = 2
[[languages.en.menu.main]]
identifier = 'home'
name = 'Home'
url = '/'
|
File naming convention for multilingual content:
| Filename | Language |
|---|
hello-world.md | Default (Chinese) |
hello-world.en.md | English |
Step 5: Write Content
Create Markdown files in content/posts/:
1
2
3
4
5
6
7
8
9
| ---
title: "Post Title"
date: 2026-05-28
tags: ["tag1", "tag2"]
categories: ["category"]
summary: "Short summary shown on list pages."
---
Content here...
|
Common frontmatter fields:
| Field | Purpose |
|---|
title | Post title |
date | Publication date |
tags | Tag list |
categories | Category |
summary | Summary shown on list pages |
draft | Set true to skip publishing |
weight | Sort order |
Step 6: Local Preview
Visit http://localhost:1313 for live preview with auto-reload.
Flags:
-D: Include draft posts--disableFastRender: More accurate rendering--bind 0.0.0.0: Allow LAN access
Step 7: Build and Deploy
Build Static Files
Output goes to public/ — pure static HTML/CSS/JS.
Deploy to OpenResty
Copy static files to the site directory:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
| # 1. Create site directory
mkdir -p /opt/1panel/www/blog
# 2. Copy build output
cp -r public/* /opt/1panel/www/blog/
# 3. Write Nginx config
cat > /opt/1panel/www/conf.d/blog.quickpay.wang.conf << 'EOF'
server {
listen 8026;
listen [::]:8026;
server_name blog.quickpay.wang;
root /www/blog;
index index.html;
location / {
try_files $uri $uri/ $uri/index.html =404;
}
location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml text/javascript image/svg+xml;
}
EOF
# 4. Test config
docker exec 1Panel-openresty-sWqr nginx -t
# 5. Reload
docker exec 1Panel-openresty-sWqr nginx -s reload
|
One-Click Deploy Script
Create deploy.sh:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| #!/bin/bash
set -e
echo "Building Hugo site..."
hugo --minify
echo "Deploying to OpenResty..."
cp -r public/* /opt/1panel/www/blog/
echo "Testing nginx config..."
docker exec 1Panel-openresty-sWqr nginx -t
echo "Reloading nginx..."
docker exec 1Panel-openresty-sWqr nginx -s reload
echo "Done! Site deployed."
|
Then deploy with:
Comparison: Hugo vs Next.js
| Aspect | Next.js (old) | Hugo (new) |
|---|
| Build time | ~30s+ | ~160ms |
| Runtime deps | Node.js container | None (static) |
| Deployment | Docker Compose + Nginx | Copy files to web dir |
| Memory usage | ~200MB+ | 0 (static files) |
| Server requirements | Running Node process | Just a web server |
| Content format | MDX | Markdown |
| Theme ecosystem | Build your own | Rich community themes |
Final Result
Blog URL: http://blog.quickpay.wang:8026/
Features:
- Chinese/English language switching
- Dark/light mode (auto system detection)
- Post tags and categories
- Code highlighting with copy button
- Table of contents navigation
- RSS feed
- SEO-friendly sitemap
Quick Reference
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| # New post
hugo new content posts/my-new-post.md
# Local preview (with drafts)
hugo server -D
# Build
hugo --minify
# Deploy
./deploy.sh
# Update theme
git submodule update --remote --merge
|