背景
一直想有一个自己的技术博客,用来记录日常折腾的东西。趁着这次有时间,在家里的 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 的管理员账号配好,再折腾一下博客的样式定制。