为什么要整理这条部署链路
博客刚搭起来的时候,最直接的办法是本地执行构建,再把 dist 放到服务器目录里。这个方式容易理解,也适合第一轮验证。
但它有一个明显问题:每次更新文章都要手动构建、手动推送、手动登录服务器同步。步骤一多,就容易忘,出错时也不好判断问题发生在哪一段。
这次我把 Risuki 博客的发布流程整理成了下面这条链路:
本地 push mainGitHub Actions 自动 builddeploy 分支自动更新阿里云服务器每 10 分钟同步 deploy 分支Nginx 展示最新静态文件这篇文章记录这条链路为什么这样设计、具体怎么配置,以及中间遇到的问题。
整体思路
这个博客是 Astro 静态站,构建后的结果就是一批 HTML、CSS、JS 和图片文件。服务器最终只需要提供这些静态文件。
所以我不希望低配服务器负责构建。更合理的分工是:
GitHub 保存源码GitHub Actions 负责构建deploy 分支保存构建产物阿里云服务器只负责同步和提供静态文件Nginx 负责对外访问这样服务器上不需要跑 pnpm install,也不需要维护复杂的 Node.js 构建环境。构建失败就看 GitHub Actions,网站打不开再看 Nginx 和服务器。
GitHub Actions 自动构建
仓库里新增了 .github/workflows/deploy.yml,核心职责是:
- 监听
main分支。 - 安装依赖。
- 执行
pnpm build。 - 把
dist目录发布到deploy分支。
关键流程大概是:
on: push: branches: [main] workflow_dispatch:
permissions: contents: write这里有两个点比较重要。
第一个是 workflow_dispatch。它表示这个 workflow 支持手动触发。后续如果想重新部署一次,不一定非要再提交代码。
第二个是 contents: write。因为 workflow 要把构建后的 dist 推送到 deploy 分支,所以 GitHub Actions 需要仓库内容写权限。
首次使用前,还需要在 GitHub 仓库里确认:
Settings -> Actions -> General -> Workflow permissions选择:
Read and write permissionsdeploy 分支只放构建产物
我没有把源码直接放到服务器上构建,而是让 deploy 分支只保存构建后的成品。
发布时在 dist 目录内部初始化 Git:
git -C dist init --initial-branch=deploygit -C dist add -Agit -C dist commit -m "deploy: publish ${GITHUB_SHA}"git -C dist push --force origin deploy这些命令的含义是:
git -C dist ...:在dist目录里执行 Git 命令,而不是在源码根目录执行。git init --initial-branch=deploy:把dist初始化成一个新的 Git 仓库,分支名叫deploy。git add -A:把构建产物全部加入暂存区。git commit ...:生成一次部署提交。git push --force origin deploy:用这次构建产物覆盖远端deploy分支。
这里使用 --force 是有意的。deploy 分支不是人工维护的历史分支,而是“当前最新构建产物”的快照。
服务器为什么不能继续 git pull
一开始我在服务器执行:
git pull origin deploy结果 Git 提示:
fatal: Need to specify how to reconcile divergent branches.原因是 GitHub Actions 每次都会强制更新 deploy 分支。服务器本地的 deploy 和远端 deploy 历史可能不再是一条直线。普通 git pull 不知道应该合并、变基还是只允许快进。
既然服务器目录只是构建产物目录,就不应该在这里做 merge。正确做法是直接让服务器目录等于远端 deploy:
git fetch origin deploygit reset --hard origin/deploy含义:
git fetch origin deploy:获取远端deploy分支最新状态,但暂时不改当前文件。git reset --hard origin/deploy:把当前目录强制同步成远端deploy的内容。
这个目录只保存静态文件,不在服务器上手动改内容,所以这里使用 reset --hard 是合理的。
服务器自动同步脚本
服务器上的站点目录是:
/var/www/risukio.com/current我创建了一个更新脚本:
sudo nano /usr/local/bin/update-risukio.sh脚本内容:
#!/bin/bashset -e
SITE_DIR="/var/www/risukio.com/current"LOG_PREFIX="[risukio deploy]"
echo "$LOG_PREFIX start: $(date '+%F %T')"
cd "$SITE_DIR"
/usr/bin/git fetch origin deploy/usr/bin/git reset --hard origin/deploy
echo "$LOG_PREFIX done: $(date '+%F %T')"这里每一段都有明确作用:
#!/bin/bash:指定用 bash 执行脚本。set -e:脚本中任何命令失败就立刻停止。SITE_DIR=...:保存站点目录路径。LOG_PREFIX=...:统一日志前缀,后续排查时更容易看。cd "$SITE_DIR":进入 Nginx 正在读取的静态站目录。/usr/bin/git fetch ...:拉取远端deploy状态。/usr/bin/git reset --hard ...:同步到远端构建产物。
写完后给脚本执行权限:
sudo chmod +x /usr/local/bin/update-risukio.sh含义:
chmod +x:给脚本增加可执行权限。- 没有这一步,系统可能不能直接运行这个脚本。
手动测试:
sudo /usr/local/bin/update-risukio.sh正常输出类似:
[risukio deploy] start: 2026-06-17 16:25:22From github-blog:Fantuan276/Blog * branch deploy -> FETCH_HEADHEAD is now at 71b2560 deploy: publish 6a962cc06cd30bed96d099a90bde6b1e193cf510[risukio deploy] done: 2026-06-17 16:25:26cron 每 10 分钟自动执行
脚本能手动执行后,再交给 cron 定时执行。
编辑 root 用户的定时任务:
sudo crontab -e加入:
*/10 * * * * /usr/local/bin/update-risukio.sh >> /var/log/risukio-deploy.log 2>&1含义:
*/10 * * * *:每 10 分钟执行一次。/usr/local/bin/update-risukio.sh:执行刚才写好的同步脚本。>> /var/log/risukio-deploy.log:把脚本输出追加到日志文件。2>&1:把错误输出也写入同一个日志文件。
保存后可以查看当前 crontab:
sudo crontab -l再查看日志:
tail -n 50 /var/log/risukio-deploy.log实际验证时,日志里已经出现了:
16:30:0116:40:0116:50:01说明 cron 确实每 10 分钟自动执行一次。
关于流量计费
我的阿里云实例是按使用流量计费,重点是公网出网流量。
服务器定时执行:
git fetch origin deploygit reset --hard origin/deploy主要是服务器从 GitHub 下载内容,这属于入网方向。按当前计费方式,入网通常不计入公网出网费用。
真正消耗出网流量的是用户访问网站时,服务器把页面、脚本、样式、图片返回给浏览器。
而且没有更新时,git fetch 只交换少量 Git 元信息。个人博客每 10 分钟轮询一次,流量压力很小。
现在的日常发布方式
现在更新博客只需要在本地:
git add .git commit -m "content: update posts"git push origin main之后流程会自动走完:
main 分支更新GitHub Actions 构建deploy 分支更新服务器 cron 同步Nginx 展示新页面如果一切正常,以后不需要每次登录服务器。
后续可以继续优化什么
当前这个方案不是最实时的,因为服务器最多要等 10 分钟才会同步。但它很稳,权限也简单。
后续如果想做到 push 后立刻更新,可以把最后一步改成:
GitHub Actions 构建成功后,通过 SSH 登录服务器执行 update-risukio.sh那样体验会更接近“真正自动发布”。不过 SSH key、GitHub Secrets、服务器权限都要处理好。对当前个人博客来说,10 分钟 cron 已经足够实用。
小结
这次部署链路整理下来,核心不是某一条命令,而是把职责分清楚:
本地负责写文章GitHub 负责保存源码GitHub Actions 负责构建deploy 分支负责保存成品服务器负责同步成品Nginx 负责提供访问每一层只做一件事,后面排查问题也会清楚很多。
如果这篇文章对你有帮助,欢迎分享给更多人!
部分信息可能已经过时






