把博客搬上 Ubuntu 24.04:一次部署复盘,以及踩到的六个坑

不吆 · 2026-11-07

搬一次站,比读十篇部署教程有用。

这台机器从一台空白的 Ubuntu 24.04 开始,装依赖、配 Caddy、接数据库、绑域名,中间踩了六个坑。记下来,下次换机器直接用。

先给结论

真正花时间的从来不是「装什么」,而是三件事:

  1. 权限边界——谁能在哪写什么;
  2. 域名与证书——DNS 不在你手里的时候,自动化证书就是一个会失败的定时任务;
  3. 数据——备份、恢复、以及「恢复之后为什么还是空的」。

代码和配置都是可以重来的,数据不是。

一、权限:NoNewPrivs 那一刀

第一次跑 sudo 得到这样一句:

sudo: The "no new privileges" flag is set, which prevents sudo from running as root.

不是没配密码,而是内核层面禁用了 setuid 提权(NoNewPrivs=1)。这种限制一旦由父进程设下,子进程只能继承,无法撤销。检查方法很简单:

grep NoNewPrivs /proc/self/status

对运维来说这条约束的意义在于:如果一个进程本来就跑在受限上下文里,任何「脚本里 sudo 一下」的部署方案都会失败。部署脚本必须准备好「无 root 也能跑」的降级路径,否则一定会在某个环境里卡住。

二、Caddy:证书是自动的,DNS 不是

Caddy 拿证书确实省心,buyao.online { ... } 写完重启,剩下的它自己办。日志里能看到完整过程:

trying to solve challenge  identifier=buyao.online  challenge_type=http-01
authorization finalized    authz_status=valid
certificate obtained successfully

但自动化只覆盖「证书」这一段,域名解析仍然是人手动配的。这次就出一个事故:加 www 记录时,把原来的 @ 记录覆盖掉了。表现很有意思——

排查时最有用的一条证据是:看服务端日志里有没有请求进来。没有请求,问题就在 DNS 或网络,不在服务。

dig +short @amos.dnspod.net buyao.online A   # 直接问权威服务器,绕开所有缓存

三、systemd 硬化与它的副作用

服务单元里加这些是好事:

NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=read-only
ReadWritePaths=/home/dsh/demo/buyao-blog/server/data

但它们同时定义了「服务能看见什么」:ProtectHome=read-only 让 /home 只读,ReadWritePaths 单独放行数据目录。于是代码放哪、数据放哪,必须在部署前就想好,不能等报错了再挪。

另一个容易忽略的是 WorkingDirectory 与相对路径的关系。Node 服务里任何 ./data 都相对它解析,而不是相对仓库根。

四、SQLite:WAL、备份与「先停服务再恢复」

单机小站用 SQLite 完全够用,但它的两个特性必须理解:

WAL 模式下,最近的写入可能还在 -wal 文件里。 直接 cp blog.db 会丢数据,正确做法是用 VACUUM INTO 做在线快照:

sqlite3 blog.db "VACUUM INTO '/tmp/snapshot.db'"

热恢复会把数据搞丢。 如果旧连接还持着数据库文件,它退出时的 WAL checkpoint 可能把旧的数据页写回你刚替换的新文件。表现就是:

恢复显示成功,重启之后文章变 0。

所以恢复流程必须是「停服务 → 换库 → 起服务」,没有例外。

还有个很隐蔽的坑:手工备份和定时备份落在了不同目录。定时器通过环境变量指定备份目录,手工执行时却回落到仓库内的默认路径。结果就是照着文档去 ~/backups/db 取「最新备份」,拿到的其实是当天凌晨的旧快照。这类问题不会报错,只会让你在恢复之后隐约觉得「好像少了几条评论」。

五、图片:从 835KB 到 97KB

首屏最贵的一笔流量通常是头图。一张 1280×1700 的竖图原图 835KB,而首页主卡实际显示宽度不到 900px。

做法是上传时顺手生成两档派生图:

档位 尺寸 体积
原图 1280×1700 835 KB
头图档 1280 宽 WebP 97 KB
卡片档 480 宽 WebP 28 KB

前端优先取派生图,缺失时 onerror 回退原图。没有 ffmpeg 也不能让上传失败——这只是优化项。

竖图还有第二个问题:object-fit: cover 只露出中间一条,主体经常被裁。这个用存一个焦点坐标解决,前端按 object-position 渲染,后台可以拖着调。

六、备份:同盘备份等于没有备份

/home/dsh/backups 和数据库在同一块盘上。盘坏了、误删了、整机重装,数据和备份一起消失。

异地备份最简单的形态是本地拉取:服务器在 NAT 后面,推不出去,那就让本地电脑定时来拉。

# 本地执行
rsync -avz -e "ssh -i ~/.ssh/buyao-backup" \
  buyao-backup@<host>:/srv/buyao-backup/ ~/buyao-backup/

服务器上那个账号只读、只能密钥登录、读不到其他目录。备份里含密码哈希和有效会话,等同于账号凭据,放哪儿都要当密钥对待。

最后的清单

部署这件事,做对一次不难,难的是下次别人照着做也能对。所以这次改动都进了仓库,命令都写进了文档——包括上面这些坑。

不吆的博客

BUYAO / NOTES独立写作 · 持续学习

不吆的博客

在代码、设计与生活之间,记录缓慢而具体的思考。

FIND A NOTE

搜索文章

输入关键词,在全部文章中查找。
按 Ctrl / ⌘ K 随时打开Enter 打开首条结果
–
篇文章
–
条回应
–
个标签
RECENT / 新近

最新文章

长文阅读

BUYAO / JOURNAL

回应
PRIVATE DESK

后台登录

发布、编辑、删除需要登录。访客浏览与留言无需登录。
博客后台使用站长账号;刷题学习账号是另一套身份。

密码经后端加盐哈希校验,浏览器不会保存任何可写凭证。
这里登录的是博客后台,请使用站长账号;刷题属于另一套学习账号,请到 刷题登录页。
WRITING DESK

写一篇新文章

新文章
0 = 默认;1、2、3… 越小越靠前

置顶后会出现在首页「编辑精选」,并排在列表最前。

    只保存选中的标签;搜索结果里没有时,可以在菜单底部新建一个。

    未设置封面
    链接地址(slug)
    发布后地址为 /post/<slug>,可被搜索引擎收录。
    0 字 · 约 1 分钟
    正文插图有三种方式:点「插入图片」、直接把图片拖进编辑框、或粘贴截图。
    从飞书/语雀导出的 Markdown:点「导入 Markdown」把 .md 和图片一起选中(也支持含图片的 .zip),或点「导入文件夹」直接选整个导出目录——图片会上传到本站并自动改好链接;外链图片会尝试抓回本地,抓不到的保留原链接。
    如果正文已经贴好了、只有图片是「图片和附件/xxx.png」这种本地路径:点「修复正文图片」,选中同名的图片文件,会原位替换,不会动你的正文。
    文章保存到本站后端数据库(SQLite)。Ctrl/⌘ + S 也可保存;顶栏「备份」可导出全部数据为 JSON 文件。
    ⌘/Ctrl + S 保存

    站点设置

    标签决定文章怎么被分类和筛选。分组会出现在标签页上(如「技术栈」「设计与生活」);图标/封面图会显示在标签卡和筛选条里。