从零搭建个人技术博客:Hugo + Gitea + Docker 全栈部署记录

记录在 Proxmox 家庭服务器上,使用 Hugo、Gitea Actions、Waline、Caddy 搭建个人博客的完整过程,以及途中踩过的坑。

背景

一直想有一个自己的技术博客,用来记录日常折腾的东西。趁着这次有时间,在家里的 Proxmox 集群上从零搭了一套,全程 Self-hosted,没有依赖任何云服务。

最终架构

两台机器,分工明确:

机器角色
lxc (10.10.10.61)Caddy v2 网关,负责 HTTPS 和反向代理
debian (10.10.10.62)应用服务器,跑所有 Docker 服务

完整的部署流程:

本地写 Markdown
  → git push
  → Gitea (git.ruisv.com)
  → Gitea Actions 触发
  → act_runner 拉起 hugomods/hugo:exts 容器构建
  → 输出到 /apps/blog/public
  → nginx 容器对外提供服务
  → Caddy 反代 ruis.hk → nginx:8080

评论系统独立部署:

ruis.hk 页面加载 Waline JS
  → 请求 comments.ruis.hk
  → Caddy 反代 → Waline 容器:8360
  → SQLite 存储

服务一览

所有服务全部跑在 Docker 里,主机只挂载数据目录:

~/apps/
├── gitea/          Gitea 代码托管
├── gitea-runner/   Gitea Actions Runner
├── blog/           nginx 静态文件服务
│   └── public/     Hugo 构建输出
└── waline/         评论系统
    └── data/       SQLite 持久化

踩过的坑

1. Gitea 内置 SSH Server 没有启动

现象:端口 2222 在监听,但连接直接被 reset。

原因:app.ini 缺少 SSH_SERVER_HOST_KEYS 配置,Gitea 找不到存放主机密钥的路径,SSH Server 静默跳过启动。

修复:

[server]
SSH_SERVER_HOST_KEYS = ssh/gitea.rsa,ssh/gitea.rsa.pub

2. LXC 容器内 nftables NAT 不生效

现象:在 lxc 里配好了 nftables DNAT 规则,规则看起来正常,但实际流量没有被转发。

原因:Proxmox LXC 容器共享宿主机内核,容器内的 nftables NAT hook 无法正常工作。

修复:换用 socat 在应用层做 TCP 转发,绕开内核 NAT 限制:

socat TCP-LISTEN:2222,fork,reuseaddr TCP:10.10.10.62:2222

用 systemd 管理,开机自启,稳定可靠。

3. Gitea Actions 构建失败:Duplicate mount point

现象:第一次 push 触发构建,报错 Error response from daemon: Duplicate mount point: /blog-public

原因:runner 的 config.yml 里通过 container.options 挂载了 /blog-public,workflow 文件里的 container.volumes 又挂载了一次,Docker 不允许同一路径挂载两次。

修复:去掉 workflow 里的 container.volumes,只保留 runner config 里的挂载。

4. Docker 创建的文件 root 权限

现象:用 docker run hugomods/hugo:exts new site . 初始化站点后,生成的文件全是 root 所有,无法直接编辑。

修复:

sudo chown -R ruis:ruis ~/apps/blog/src

5. Caddy 泛域名证书优化

原本每个子域名单独一张证书,Caddyfile 里 AliDNS 凭据重复了六次。

优化后:

  • *.ruisv.com 统一一张泛域名证书,所有子域名自动复用
  • 提取 (tls_ruis_hk)(proxy_headers) snippet,消除重复配置

写文章流程

# 新建文章
hugo new content post/文章标题/index.md

# 编辑内容(Markdown)
vim content/post/文章标题/index.md

# 推送,自动部署
git add . && git commit -m "post: 文章标题" && git push

push 之后大概 30 秒,文章就出现在 ruis.hk 上。

小结

整套方案完全 Self-hosted,没有月租费用,数据在自己手里。Gitea Actions 的体验和 GitHub Actions 基本一致,Hugo 构建速度很快,Stack 主题开箱即用。

后续计划把 Waline 的管理员账号配好,再折腾一下博客的样式定制。