从零配置 Firefly 博客:部署过程与踩坑记录
前言
最近给自己部署了一个 Firefly 博客,并将它命名为「魂灵宝藏」。
Firefly 是一个基于 Astro 的静态博客主题。博客访问时不需要服务器实时生成页面,因此速度快、资源占用低,也比较容易维护。
不过,“静态博客”只是指网站运行时是静态的,并不代表构建过程也很轻量。实际部署过程中,我就在一台 2 核、约 2GB 内存的服务器上遇到了构建卡顿、内存压力过大,以及服务器暂时无法连接的问题。
这篇文章记录一下完整的配置过程,以及最后为什么改成了 GitHub Actions 自动构建。
最初的服务器环境
服务器的基本配置如下:
- Ubuntu 24.04
- 2 核 CPU
- 约 2GB 内存
- 40GB 硬盘
- Nginx
- Node.js 22
- pnpm 9.14.4
Firefly 的源代码需要通过 Node.js 和 pnpm 安装依赖,然后使用 Astro 生成最终的静态页面。
基本流程是:
pnpm installpnpm build构建成功后,会生成一个 dist 目录。里面是 HTML、CSS、JavaScript、图片和搜索索引等静态文件,Nginx 只需要把这个目录提供给访问者即可。
第一次尝试:直接在服务器上构建
最开始采用的是最直接的方案:
- 把 Firefly 源代码放到服务器。
- 安装 Node.js 和 pnpm。
- 在服务器上运行构建命令。
- 让 Nginx 指向构建完成的目录。
为了避免内存不足,还提前创建了 Swap,并限制了 Node.js 可使用的内存:
NODE_OPTIONS=--max-old-space-size=1400 pnpm build但实际构建时,服务器很快变得非常卡顿。
SSH 连接开始超时,网页控制台也很难打开,看起来就像服务器直接“死机”了一样。重启后没有找到明确的系统级 OOM 记录,因此不能完全断定是内核杀掉了进程,但可以确认当时服务器已经处在非常明显的资源压力下。
为什么静态博客构建也会卡顿?
Firefly 不只是简单地复制几个 Markdown 文件。一次完整构建还可能包含:
- 解析 Markdown 和 MDX
- 生成所有文章页面
- 处理和压缩图片
- 生成低质量图片占位图
- 打包 CSS 和 JavaScript
- 复制或裁剪字体
- 压缩内联脚本
- 清理不需要的主题资源
- 使用 Pagefind 创建全文搜索索引
其中图片处理、JavaScript 打包和搜索索引都会占用一定的 CPU 和内存。
所以静态博客的真实运行逻辑更接近:
Markdown 与配置 ↓执行一次较重的构建 ↓生成轻量的静态文件 ↓Nginx 提供访问访问博客时很轻量,但生成博客的过程不一定轻量。
尝试限制构建资源
为了防止构建再次拖垮整台服务器,我尝试把构建任务放进单独的后台服务,并设置资源限制,例如:
- 内存软限制约 900MB
- 内存上限约 1.2GB
- Swap 上限约 1.5GB
- 限制 CPU 使用率
- 降低磁盘读写优先级
- 将 Node.js 堆内存限制为约 850MB
- 减少并行线程数量
这样做之后,服务器确实能够保持连接,不再轻易失去响应。
但是新的问题是:构建速度非常慢。
一次构建持续了一个多小时,Astro 和 esbuild 长时间占用 CPU。虽然理论上继续等待也可能完成,但这种方式显然不适合以后频繁发布文章。
最终方案:GitHub Actions 构建
最后决定不再让小型服务器承担构建任务,而是把构建工作交给 GitHub Actions。
新的发布流程是:
修改文章或配置 ↓推送到 GitHub ↓GitHub Actions 安装依赖并构建 ↓生成 dist 静态文件 ↓上传到服务器的新版本目录 ↓切换 current 软链接 ↓Nginx 立即提供新版本同一个项目在 GitHub Actions 中构建只用了不到一分钟。相比服务器本地构建一个多小时,差别非常明显。
这也说明问题并不在 Firefly 无法正常构建,而是原服务器的 CPU、内存和磁盘性能不适合承担完整构建任务。
部署目录设计
服务器上使用了类似下面的目录结构:
/var/www/firefly-site/├── current -> releases/当前版本└── releases/ ├── 版本一 ├── 版本二 └── 版本三每次部署时,GitHub Actions 会:
- 创建一个新的版本目录。
- 将本次构建结果上传进去。
- 检查关键文件是否存在。
- 将
current软链接切换到新版本。
这种方式比直接覆盖线上文件安全。如果上传过程中失败,旧版本仍然可以正常访问;只有新版本完整上传后,网站才会切换过去。
使用独立的部署用户
为了减少安全风险,没有让 GitHub Actions 直接使用服务器的 root 用户。
服务器上创建了一个权限受限的部署用户,它只能操作博客部署目录,没有 sudo 权限。GitHub 中保存的则是专门用于部署的 SSH 私钥和服务器连接信息。
需要保存的内容通常包括:
- 服务器地址
- SSH 端口
- 部署用户名
- SSH 私钥
这些信息应该放在 GitHub Actions Secrets 中,不能直接写进公开仓库。
使用 Nginx 提供静态文件
Nginx 指向 current 目录,而不是某个固定版本:
server { server_name thesoullove.fun;
root /var/www/firefly-site/current; index index.html;
location / { try_files $uri $uri/ /404.html; }
location ~* \.(css|js|png|jpg|jpeg|gif|svg|webp|woff|woff2)$ { expires 30d; add_header Cache-Control "public"; }}这样每次切换 current 软链接后,Nginx 不需要重新安装,也不需要重新启动整个服务器。
域名与 HTTPS
域名解析到服务器后,再通过 Certbot 为以下地址申请 HTTPS 证书:
thesoullove.funwww.thesoullove.fun
最终让 HTTP 自动跳转到 HTTPS,并将 www 地址统一跳转到主域名。
配置完成后,访问者看到的就是:
https://thesoullove.fun以后如何发布文章
采用 GitHub Actions 后,日常维护会简单很多。
新增文章时只需要:
- 新建 Markdown 文件。
- 填写标题、日期、分类和标签。
- 编写正文。
- 提交并推送到 GitHub。
- 等待自动构建和部署完成。
仍然需要执行一次构建,但不需要在自己的电脑或服务器上手动执行。GitHub 会自动完成这些工作。
分类和标签也不是单独维护的固定页面。Firefly 会读取文章头部的 category 和 tags 信息,在构建过程中自动生成对应页面。
如果没有任何文章使用分类或标签,可以把相关入口隐藏;以后添加了分类或标签,下一次构建时再自动显示。
这次部署得到的经验
静态网站不代表构建过程很轻
静态网站上线后的资源占用很低,但构建时仍然可能需要大量 CPU、内存和磁盘读写。
小型服务器更适合提供文件
对于 2 核、2GB 左右的服务器,让它负责 Nginx 和静态文件访问非常合适,但没有必要同时让它承担依赖安装、图片处理和前端打包。
Swap 只能缓解问题
Swap 可以降低进程因为内存不足而被终止的概率,但磁盘速度远低于内存。大量使用 Swap 后,服务器可能不会立即崩溃,却会变得非常卡顿。
自动化构建更适合长期维护
将源码和文章保存在 GitHub,由 GitHub Actions 构建,再将结果部署到服务器,可以同时获得:
- 更快的构建速度
- 更稳定的服务器
- 可追踪的修改记录
- 更安全的部署过程
- 更方便的版本回退
总结
Firefly 最终采用的是“GitHub 负责构建,服务器负责运行”的方案。
这个结构比较适合低配置服务器:平时运行博客几乎不需要多少资源,而发布新文章时,也不会再因为构建任务导致服务器卡顿或连接中断。
整个过程里最大的误区,就是一开始认为静态博客的构建也一定很轻量。实际体验后才发现,把构建和运行分开,才是更稳定、更适合长期维护的方式。
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!














