背景

这次用 Docker 方式部署 Hugo 博客,避免在本地安装 Hugo 环境,同时支持热重载方便写作。本文记录从零开始到生产部署的完整过程。

环境信息

  • 系统:macOS
  • 博客目录:/Users/xhsui/Library/CloudStorage/SynologyDrive-content/Hugo
  • 容器镜像:hugomods/hugo:exts(支持 Sass/SCSS)
  • 主题:PaperMod
  • 域名:<ADDRESS_INFO_REPLACED>

完整部署步骤

1. 设置环境变量

export HUGO_PATH="/Users/xhsui/Library/CloudStorage/SynologyDrive-content/Hugo"

2. 初始化 Hugo 站点

docker run --rm \
  -v "${HUGO_PATH}:/src" \
  hugomods/hugo:exts \
  new site . --force

3. 安装主题(PaperMod)

cd "${HUGO_PATH}"
git clone https://github.com/adityatelange/hugo-PaperMod.git themes/PaperMod

4. 创建配置文件

创建 hugo.toml(注意:Hugo 0.110+ 推荐使用 .toml 格式):

baseURL = 'https://blog.xhsui.com/'
title = 'xhsui blog'
theme = 'PaperMod'
defaultContentLanguage = 'zh-cn'
enableInlineShortcodes = true

[params]
  locale = 'zh-cn'
  mainSections = ["tech", "health"]
  showSummary = true
  showReadingTime = true
  ShowShareButtons = false
  ShowPostNavLinks = true
  ShowCodeCopyButtons = true

[params.homeInfoParams]
  Title = "欢迎来到我的博客"
  Content = "这里记录了我的技术学习和健康生活点滴。"

# 菜单配置
[menu]
  [[menu.main]]
    identifier = "tech"
    name = "技术"
    url = "/tech/"
    weight = 10
  
  [[menu.main]]
    identifier = "health"
    name = "健康"
    url = "/health/"
    weight = 20
  
  [[menu.main]]
    identifier = "tags"
    name = "标签"
    url = "/tags/"
    weight = 30

[pagination]
  pagerSize = 10

⚠️ 关键配置说明:

配置项 说明
defaultContentLanguage = 'zh-cn' 语言代码必须全小写,否则 Hugo 会报错
mainSections = ["tech", "health"] 告诉 PaperMod 在首页显示哪些 section
baseURL 生产环境用真实域名,本地开发用 http://localhost:1313/

5. 创建第一篇文章

docker run --rm \
  -v "${HUGO_PATH}:/src" \
  hugomods/hugo:exts \
  new content/posts/hello-world.md

6. 修改文章状态(draft 改为 false)

sed -i '' 's/draft = true/draft = false/g' "${HUGO_PATH}/content/posts/"*.md

7. 启动容器(支持热重载)

# 删除旧容器(如果存在)
docker stop hugo 2>/dev/null
docker rm hugo 2>/dev/null

# 启动新容器
docker run -d \
  --name hugo \
  --restart unless-stopped \
  -p 1313:1313 \
  -v "${HUGO_PATH}:/src" \
  hugomods/hugo:exts \
  server --bind "0.0.0.0" --poll 1s --disableFastRender

8. 验证运行状态

docker logs hugo --tail 20

访问 http://localhost:1313 即可看到博客。


常用管理命令

操作 命令
查看日志 docker logs hugo
停止容器 docker stop hugo
启动容器 docker start hugo
重启容器 docker restart hugo
手动触发编译 docker exec hugo hugo --quiet
查看容器状态 docker ps | grep hugo

清理缓存

如果需要完全清理缓存重新生成:

# 停止并删除容器
docker stop hugo
docker rm hugo

# 清理缓存文件
rm -rf "${HUGO_PATH}/resources/_gen"
rm -rf "${HUGO_PATH}/.hugo_build.lock"
rm -rf "${HUGO_PATH}/public"

# 重新启动容器
docker run -d \
  --name hugo \
  --restart unless-stopped \
  -p 1313:1313 \
  -v "${HUGO_PATH}:/src" \
  hugomods/hugo:exts \
  server --bind "0.0.0.0" --poll 1s --disableFastRender

关键参数说明

参数 作用
-d 后台运行
--name hugo 容器命名为 hugo
--restart unless-stopped 自动重启(手动停止除外)
-p 1313:1313 端口映射
-v 目录挂载
--bind "0.0.0.0" 允许外部访问
--poll 1s 每秒轮询文件变化(适合云存储目录)
--disableFastRender 每次修改完整重新渲染

热重载说明

由于博客目录在云存储(SynologyDrive)上,文件变化通知可能不灵敏,使用 --poll 1s 参数让 Hugo 每秒主动扫描一次文件变化。

工作流程:

  1. 编辑 .md 文章文件并保存
  2. 等待 1 秒
  3. 刷新浏览器即可看到变化

遇到的问题及解决

问题 1:找不到配置文件

报错: Unable to locate config file or config directory

解决: 需要先执行 new site . --force 初始化站点。

问题 2:文章不显示

原因: 新创建的文章默认 draft = true

解决:draft 改为 false,或启动时加 -D 参数。

问题 3:删除 public 目录后页面 404

解决: 手动触发编译

docker exec hugo hugo --quiet

问题 4:容器名称冲突

报错: Conflict. The container name "/hugo" is already in use

解决: 先删除旧容器

docker stop hugo && docker rm hugo

问题 5:语言代码大小写错误

报错: language name "zh-CN" is invalid: must be all lower case

解决: 改为小写 zh-cn,并使用 defaultContentLanguage 代替 languageCode(新版本 Hugo)

defaultContentLanguage = 'zh-cn'

问题 6:首页不显示文章列表

原因: PaperMod 主题不知道要显示哪个 section 的文章

解决:hugo.toml 中添加:

[params]
  mainSections = ["tech", "health"]

问题 7:.bak 备份文件被 Hugo 当成内容处理

现象: public/ 目录中出现 xxx.md.bak/ 文件夹

解决: 删除所有 .bak 文件

find "${HUGO_PATH}/content" -name "*.bak" -type f -delete

问题 8:URL 中带有端口号(生产环境)

现象: 访问 https://blog.xhsui.com/tech/ 时,页面链接指向 https://blog.xhsui.com:1313/...

原因: hugo server 默认会在 URL 后附加端口号

解决方案 A(推荐):使用静态文件 + Nginx

# 构建静态文件
cd "${HUGO_PATH}"
hugo --minify

# 把 public/ 目录部署到服务器
# Nginx 直接托管静态文件

Nginx 配置示例:

server {
    listen 443 ssl http2;
    server_name blog.xhsui.com;

    ssl_certificate     /path/to/fullchain.pem;
    ssl_certificate_key /path/to/privkey.pem;

    root /path/to/hugo/public;
    index index.html;

    location / {
        try_files $uri $uri/ =404;
    }
}

解决方案 B:继续使用 hugo server + Nginx 反代

启动 Hugo 时添加 --appendPort=false 参数:

docker run -d \
  --name hugo \
  --restart unless-stopped \
  -p 1313:1313 \
  -v "${HUGO_PATH}:/src" \
  hugomods/hugo:exts \
  server --appendPort=false --baseURL https://blog.xhsui.com/ --bind "0.0.0.0" --poll 1s --disableFastRender

Nginx 反代配置:

location / {
    proxy_pass http://127.0.0.1:1313;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
}

内容分类方案

方案选择:Section 分目录(推荐)

content/
├── tech/          ← 技术文章
│   ├── _index.md
│   └── *.md
└── health/        ← 健康记录
    ├── _index.md
    └── *.md

优势:

  • URL 结构自然:/tech//health/
  • Hugo 自动生成 section 页面
  • 便于管理和扩展

实施步骤:

  1. 创建 section 目录:
mkdir -p "${HUGO_PATH}/content/tech"
mkdir -p "${HUGO_PATH}/content/health"
  1. 移动文章:
mv "${HUGO_PATH}/content/posts/"* "${HUGO_PATH}/content/tech/"
  1. 创建 section 首页:
---
date = '2026-06-07T00:00:00+08:00'
draft = false
title = '技术'
description = '技术学习笔记和教程'
---
  1. 更新 hugo.toml
[params]
  mainSections = ["tech", "health"]
  1. 给文章添加分类标签:
for f in "${HUGO_PATH}/content/tech/"*.md; do
  sed -i '' '/draft = false/a\
categories = ["技术"]' "$f"
done

定时任务:自动生成健康日报

使用 WorkBuddy 的自动化任务,每天凌晨 3:00 自动生成健康日报并保存到 Hugo 目录:

任务配置:

  • 名称: diet-health-md-export
  • 执行时间: 每天 03:00
  • 目标文件: /Users/xhsui/Library/CloudStorage/SynologyDrive-content/Hugo/content/health/YYYY-MM-DD.md

生成的 Markdown 格式:

+++
date = "2026-06-07T03:00:00+08:00"
draft = false
title = "健康日报 - 2026-06-07"
categories = ["健康"]
+++

## 📊 今日总览

| 项目 | 实际值 | 目标值 | 完成度 |
|------|--------|--------|--------|
| 🔥 总摄入热量 | 1730 kcal | 1750 kcal | 99% ✅ |
...

好处:

  • Hugo 自动渲染,访问 /health/ 即可看到所有健康记录
  • 版本管理:所有健康数据都在 Git 中
  • 隐私可控:可配合 Nginx 设置密码保护

总结

使用 Docker 部署 Hugo 博客的好处:

  • ✅ 无需在本地安装 Hugo
  • ✅ 环境一致性
  • ✅ 支持热重载,写作体验流畅
  • ✅ 自动重启,稳定可靠
  • ✅ 配合 Nginx 反代,轻松实现生产部署

推荐的生产部署流程:

  1. 本地用 hugo server 写作和预览
  2. 写完用 hugo --minify 构建静态文件
  3. public/ 目录同步到服务器
  4. Nginx 直接托管静态文件(性能最优)

以后写博客只需要:

  1. 编辑 Markdown 文件
  2. 保存
  3. 刷新浏览器
  4. 满意后运行 hugo --minify && rsync -avz public/ server:/path/to/blog/

参考资料