博客项目概述
一、项目简介
这是一个基于 Hugo 静态网站生成器 + PaperMod 主题的个人博客系统。代码托管在 Gitee 私有仓库,通过 Caddy 作为 Web 服务器自动提供 HTTPS 服务,并利用服务器定时任务(crontab)实现自动拉取更新、构建和部署。整个系统部署在腾讯云服务器(Ubuntu 24.04)上,同时服务器还运行着 Nextcloud 私有云盘和 Jupyter 科学计算环境(均通过 Docker 容器管理)。
二、技术栈
- 静态生成器:Hugo(v 0.146.0+extended)
- 主题:PaperMod(手动下载,非 Git 子模块)
- 版本控制:Git + Gitee(国内镜像,私有仓库)
- Web 服务器:Caddy(Docker 容器),自动申请/续期 SSL 证书
- 部署方式:本地推送 → Gitee → 服务器定时拉取 → Hugo 构建 → Caddy 服务
- 服务器 OS:Ubuntu 24.04 LTS
- 容器管理:Docker + Docker Compose(管理 Caddy、Nextcloud、Jupyter 等)
- 自动部署:crontab 每分钟执行自定义脚本
/home/your-username/auto-deploy.sh
三、系统架构
本地电脑
│ 写文章 (Markdown)
│ hugo new posts/xxx.md
│ 编辑后 git add/commit/push
▼
Gitee 仓库 (私有)
│
▼
腾讯云服务器
├─ crontab (每分钟)
│ └─ 运行 /home/your-username/auto-deploy.sh
│ ├─ git pull 拉取最新源码
│ ├─ hugo --minify 生成静态文件到临时目录
│ ├─ 替换 /home/your-username/blog/public
│ ├─ 修复权限为 www-data
│ └─ docker restart caddy
└─ Caddy 容器 (监听 80/443)
└─ 托管 /home/your-username/blog/public
四、关键配置与路径
4.1 本地博客源码
- 路径:
本地博客路径 - 配置文件:
hugo.toml(或config.toml),内容示例:baseURL = 'https://lingzuian.top/' languageCode = 'zh-cn' title = '静影沉璧到此一游' theme = 'PaperMod' copyright = '© 2026 静影沉璧到此一游 | 备案号:<a href="https://beian.miit.gov.cn/" target="_blank">湘ICP备2026005470号</a>' [params] math = true # 开启 KaTeX 数学公式 [menu] [[menu.main]] name = "文章" url = "/posts/" weight = 10 [[menu.main]] name = "关于" url = "/about/" weight = 20 - 文章存放:
content/posts/ - 页面存放:
content/,如about.md - 自定义模板:
layouts/partials/,如math.html(加载 KaTeX)
4.2 服务器关键目录
- 博客源码:
/home/your-username/blog-source(从 Gitee 克隆) - 静态文件目录:
/home/your-username/blog/public(Caddy 挂载的根目录) - 自动部署脚本:
/home/your-username/auto-deploy.sh - 部署日志:
/home/your-username/auto-deploy.log - Caddy 配置:
/home/your-username/caddy/Caddyfile - Caddy Compose 文件:
/home/your-username/caddy/docker-compose.yml
4.3 自动部署脚本(当前稳定版)
#!/bin/bash
SOURCE_DIR="/home/your-username/blog-source"
PUBLIC_DIR="/home/your-username/blog/public"
TEMP_DIR="/home/your-username/blog/temp"
cd "$SOURCE_DIR" || exit 1
git fetch origin
LOCAL=$(git rev-parse HEAD)
REMOTE=$(git rev-parse origin/main)
if [ "$LOCAL" = "$REMOTE" ]; then
echo "No updates, exiting."
exit 0
fi
echo "Updates found, pulling changes..."
if ! git pull; then
echo "Git pull failed. Aborting deployment."
exit 1
fi
mkdir -p "$TEMP_DIR"
sudo chown your-username:your-username "$TEMP_DIR"
if ! hugo --minify --destination "$TEMP_DIR"; then
echo "Hugo build failed, aborting deployment. Keeping old site."
rm -rf "$TEMP_DIR"
exit 1
fi
sudo rm -rf "$PUBLIC_DIR"
sudo mv "$TEMP_DIR" "$PUBLIC_DIR"
sudo chown -R 33:33 "$PUBLIC_DIR"
sudo chmod -R 755 "$PUBLIC_DIR"
docker restart caddy
echo "Deployment complete."
4.4 Caddy 配置 (Caddyfile)
lingzuian.top {
root * /var/www/blog
file_server
}
cloud.internal.example.com {
reverse_proxy nextcloud_app:80
}
jupyter.internal.example.com {
reverse_proxy math_jupyter:8888
}
4.5 Caddy Docker Compose (docker-compose.yml)
services:
caddy:
image: caddy:latest
container_name: caddy
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- /home/your-username/blog/public:/var/www/blog
- caddy_data:/data
- caddy_config:/config
networks:
- web
volumes:
caddy_data:
caddy_config:
networks:
web:
external: true
五、自动部署流程详解
- crontab 每分钟执行
/home/your-username/auto-deploy.sh。 - 脚本先
git fetch origin比较本地与远程 commit hash:- 若相同 → 退出(无更新)。
- 若不同 → 执行
git pull拉取最新代码。
- 拉取成功后,创建临时目录
/home/your-username/blog/temp,确保所有者是your-username。 - 运行
hugo --minify --destination生成静态文件到临时目录。 - 构建成功:删除旧
public目录,将temp原子移动为public,然后修复所有者为www-data(UID 33)并设置权限 755,最后重启 Caddy。 - 构建失败:保留旧
public目录,删除临时目录,退出(网站不变)。
六、常见问题与解决方案(历史坑点)
6.1 权限问题(Permission denied)
- 现象:Hugo 构建时无法写入
public目录,或 Caddy 无法读取文件。 - 原因:
public目录所有者不是运行 Hugo 的用户(your-username)或不是容器内的用户(www-data)。 - 解决方案:
- 在脚本中,生成前确保
temp目录归your-username,生成后public归www-data。 - 手动修复:
sudo chown -R 33:33 /home/your-username/blog/public
- 在脚本中,生成前确保
- 当前状态:脚本已正确处理,若手动操作后需检查权限。
6.2 Git 冲突或无法推送
- 现象:
git push被拒,提示non-fast-forward或divergent branches。 - 原因:在服务器上直接提交了代码,导致本地与远程分叉。
- 解决方案:
- 永远不要在服务器上执行
git commit。所有修改必须在本地进行。 - 若已发生,需在服务器上
git pull合并(可能冲突),再git push解决。
- 永远不要在服务器上执行
- 预防:遵守“只从本地推送”原则。
6.3 Hugo 构建失败(Front Matter 格式错误)
- 现象:构建时报错
unmarshal failed: toml: expected character =,导致网站无法更新。 - 原因:新建或修改的 Markdown 文件开头元数据格式有误(如误用
---代替+++,或缺少等号)。 - 解决方案:
- 确保文件开头是
+++,每行格式如key = "value"。 - 推送前用
hugo server -D本地预览,可提前发现错误。
- 确保文件开头是
- 脚本保护:脚本在构建失败时会保留旧网站,避免 404。
6.4 Caddy 挂载失效(目录 inode 变化)
- 现象:网站 404,但服务器上
public目录有文件,容器内/var/www/blog为空。 - 原因:脚本用
sudo rm -rf删除旧目录再重建,导致目录 inode 变化,Docker 挂载指向了已删除的 inode。 - 解决方案:
- 改用
sudo rm -rf "$PUBLIC_DIR"/*清空内容而不删除目录本身(保留 inode)。 - 或采用临时目录原子替换(当前脚本已使用
mv替换整个目录,但mv后 inode 改变,仍需重启 Caddy)。因此脚本最后重启 Caddy 确保重新挂载。
- 改用
- 当前状态:脚本通过重启 Caddy 解决此问题,且只在有更新时重启,无更新时不重启。
6.5 自动部署脚本误判更新
- 现象:即使无更新,脚本也执行构建和重启。
- 原因:早期脚本通过
git pull的输出判断更新,不够准确。 - 解决方案:改用
git fetch比较 commit hash(当前脚本已修复)。
6.6 KaTeX 公式不渲染
- 现象:文章中的
$...$或$$...$$未渲染为数学公式。 - 原因:KaTeX 脚本未正确加载或定界符未配置。
- 解决方案:
- 在
layouts/partials/math.html中添加 KaTeX 脚本和renderMathInElement调用(已配置)。 - 确保文章或全局开启了
math = true。
- 在
七、注意事项与最佳实践
- 绝对不要在服务器上直接修改文件(包括
nano编辑),所有更改都应在本地完成并推送。 - 绝对不要在 Gitee 网页上直接编辑文件,以免造成版本混乱。
- 推送前务必本地预览:
hugo server -D检查格式和渲染。 - 新文章必须
git add:新建文件后,先用git status确认已加入版本控制。 - 定期查看服务器日志:
tail -f /home/your-username/auto-deploy.log确保脚本正常运行。 - 如果需紧急修改服务器文件(不推荐),改完后必须立即
git commit并git push,然后在本地git pull同步。
八、日常维护与更新指南
8.1 写一篇新文章
# 本地
cd 本地博客路径
hugo new posts/文章名.md
# 编辑文章(修改 draft = false)
hugo server -D # 预览
# 满意后
git add .
git commit -m "新增文章:文章名"
git push
8.2 修改已有文章
# 编辑文件后
git commit -am "更新文章:xxx" # 针对已追踪文件
git push
8.3 修改主题或配置
- 所有修改在本地进行,然后
git add/commit/push。 - 主题文件位于
themes/PaperMod/,若有自定义覆盖,放在layouts/或assets/css/extended/。
8.4 升级 Hugo 版本
- 服务器上需手动下载新版
hugo二进制并替换/usr/local/bin/hugo。 - 本地也需要同步升级,避免版本差异导致的构建问题。
8.5 处理服务器故障
- Caddy 挂了:
docker restart caddy或cd ~/caddy && docker-compose down && docker-compose up -d - 磁盘空间满:检查日志大小,清理旧日志或无用的 Docker 镜像。
- 域名证书问题:Caddy 会自动续期,无需干预。
九、联系与备份
- 博客域名:
https://lingzuian.top(备案号:湘 ICP 备 2026005470 号) - Gitee 仓库:私有仓库,地址不公开
- 服务器:仅管理员 SSH 登录
- 数据备份:建议定期将
/home/your-username/blog/public和/home/your-username/blog-source备份到 COS 或 Nextcloud。
本概述涵盖了项目架构、配置、历史问题和维护要点。希望未来无论由谁(或哪个 AI)接手,都能快速理解并有效协助。
截止至 2026.02.14