使用 GitHub Actions + Docker 自动化部署 Astro 静态博客
694 字
3 分钟
使用 GitHub Actions + Docker 自动化部署 Astro 静态博客
动机
项目使用 Astro 构建生成 dist/ 静态文件,之前一直是手动执行 pnpm build → SCP 上传 → SSH 重启容器。每次更新都要重复这套流程,效率低且容易出错。
方案设计
采用 GitHub Actions + Docker + nginx 的自动化部署方案:
Git Push → GitHub Actions (构建) → SCP → Server (nginx:alpine)为什么不用 docker-compose?
生产环境只有一个 nginx 容器,没有后端 API、数据库或其他服务,单容器的场景上 docker-compose 是过度工程。
为什么构建不在服务器上做?
构建在 GitHub Actions 的 CI runner 上完成,服务器只做文件宿主 + 运行时启动。服务器不需要装 pnpm、Node.js,也不需要跑构建,攻击面更小、运维更轻。
工作流设计
.github/workflows/deploy-server.yml:
触发条件:push 到 master 分支步骤: 1. Checkout 代码 2. Setup Node.js 22 + pnpm 3. pnpm install --frozen-lockfile 4. pnpm build 5. SCP 上传 dist/ 到服务器 6. SSH 执行 docker run nginx:alpineSecrets 配置
| Secret | 用途 |
|---|---|
SERVER_HOST | 服务器 IP |
SERVER_USERNAME | SSH 用户名 |
SERVER_SSH_KEY | SSH 私钥 |
BLOG_DOMAIN | 博客域名或 IP(nginx 模板用) |
容器运行
服务器上直接跑 nginx:alpine 官方镜像,通过 -v 挂载静态文件目录:
docker run -d --name myFireflyBlog \ -v /path/to/blog:/usr/share/nginx/html:ro \ -p 80:80 --restart always nginx:alpineNginx 配置
通过独立的 deploy-nginx.yml workflow 管理,与博客内容部署解耦:
Gzip 压缩
gzip on;gzip_types text/plain text/css application/json application/javascript image/svg+xml;gzip_vary on;安全头
add_header X-Content-Type-Options "nosniff" always;add_header X-Frame-Options "DENY" always;add_header X-XSS-Protection "1; mode=block" always;add_header Referrer-Policy "strict-origin-when-cross-origin" always;缓存策略(针对 Astro 构建产物)
# 内容哈希文件名 —— 不可变缓存location /_astro/ { expires 365d; add_header Cache-Control "public, immutable"; }
# 通用静态资源location /assets/ { expires 30d; add_header Cache-Control "public"; }
# 搜索索引location /pagefind/ { expires 7d; add_header Cache-Control "public"; }
# HTML 页面不缓存(内容更新需即时生效)location ~* \.html$ { expires -1; add_header Cache-Control "no-cache"; }一些经验
条件挂载避免耦合
nginx 配置通过独立 workflow 管理,博客内容 deploy 时检测配置是否存在:
CONFIG_MOUNT=""if [ -f /path/to/conf/default.conf ]; then CONFIG_MOUNT="-v /path/to/conf/default.conf:/etc/nginx/conf.d/default.conf:ro"fi
docker run -d \ --name myFireflyBlog \ -v /path/to/dist:/usr/share/nginx/html:ro \ $CONFIG_MOUNT \ -p 80:80 --restart always nginx:alpine配置存在则挂载,不存在则退化到默认配置,两个 workflow 独立但不耦合。
安全加固顺手做
在配置部署的过程中顺带加固了 SSH 安全:
- 关闭密码登录(
PasswordAuthentication no) - 安装 fail2ban 防爆破
- 更换密钥对
总结
这套方案跑下来最满意的一点是变更域分离——博客内容和服务器配置走不同的 workflow,各自独立更新互不影响。改配置不需要重新构建,发文章不需要碰服务器配置。
对比之前的纯手动流程,现在每天更新博客的心理负担降到了零:写完 git push 等三分钟就好了。
支持与分享
如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!
使用 GitHub Actions + Docker 自动化部署 Astro 静态博客
https://www.sanyablog.cn/posts/deploy-astro-blog-with-github-actions/相关文章智能推荐
1
Nginx 配置与项目部署解耦:envsubst + 独立 Workflow
技术实践解决 nginx 配置和博客内容耦合在同一个 CI/CD 流程中的问题,实现各自独立更新
2
隐藏的博客文章还在首页显示?排查 Astro draft 机制问题
技术实践排查 Astro 博客中 draft: true 文章仍显示预览和分类计数错误的经历,以及 HTML 缓存策略的优化
3
让博客文章携带可下载文件:随文附件功能实现记录
博客搭建给静态博客加上随文附件下载功能:附件不随整站部署,单独上传到服务器专用目录,正文用超链接即可下载;记录了重试兜底、只读挂载点踩坑与下载限流。
4
博客 AI 助手“无回复”根因:反代网关地址缓存过期导致 502
技术实践博客 AI 助手无回复的排查经历:根因是 Nginx 反代对后端容器名解析出旧地址导致 502;通过容器内 DNS + 变量转发,让网关每次请求重新解析后端容器地址实现自愈。
5
Coze 聊天窗 z-index 与 Swup 切页适配修复
技术实践Coze 聊天窗接入 Swup SPA 切页遇到的三个坑:z-index 层级被遮挡、切页后悬浮球消失、SDK 注入样式被 SwupHeadPlugin 清空
随机文章随机推荐













