双 SQLite 文件托管平台的备份恢复实践
agentic-hosting 是让 AI Agent 在 Linux 服务器部署应用和数据库的开源工具,其平台状态存于两个 SQLite 文件。文章围绕运行中安全备份、额外需复制的数据,以及在新服务器恢复的流程展开。
AI 深度解读
托管平台用 SQLite 存状态,最该追问的未必是「能不能扛住」,而是「服务器今晚消失,能不能恢复」。agentic-hosting 的作者把控制面放进两个 SQLite 文件,通过在线备份、密钥保管和异机恢复,试图把这个问题变成可以实际验证的操作。
这套方案值得读,因为它把数据库选择拉回了运维现场。不过,「恢复两个文件」与「所有应用重新可用」之间,还有业务数据、容器镜像和重新部署几道关。
两个 SQLite 文件,保存的是平台账本
agentic-hosting 是一个让 AI Agent 在 Linux 服务器上部署应用和数据库的开源工具。它的主程序叫 ah,Agent 通过 HTTP API 请求部署应用、添加 Postgres 或 Redis、读取日志,实际执行应用的是 Docker。
这里的控制面,可以理解为平台的账本和调度员:它记录有哪些应用、应该怎样运行,再告诉 Docker 执行。
标准安装的数据目录是 /var/lib/ah。其中,ah.db 保存账户、API key、应用、构建、加密环境变量和数据库密码;ah-metering.db 保存用量;master.key 用于加解密;backups/ 存放备份。
两个 SQLite 文件保存的是控制面,应用数据库里的业务记录另有存放位置。
例如,平台创建的 Postgres 数据实际位于名为 ah-db-* 的 Docker 卷里。恢复 ah.db,只是恢复「平台知道有这个数据库」,并不会把数据库里的订单、用户或文章带回来。
拆成两个文件也有明确原因。ah 每隔 60 秒给运行中的应用采集 CPU 和内存用量,写入 usage_events、usage_daily 两张表。SQLite 每个文件同时只能有一个写入者,把持续增长的计量写入单独放置,可以减少它与部署状态更新争抢写锁的机会。
备份策略也体现了数据优先级:状态库备份失败,整次任务失败;计量库失败,只记录警告并继续。迁移文件必须以 state_ 或 metering_ 开头,否则程序拒绝启动,防止表建错位置。原文当时有 26 个状态库迁移文件、2 个计量库迁移文件,升级后的程序会在首次启动时执行尚未应用的迁移,每个迁移及其记录在同一事务内完成。
SQLite 能胜任,靠的是单机边界和锁策略
作者给出的连接配置很短,却包含这套方案的关键取舍:
sql.Open("sqlite3", path+
"?_journal_mode=WAL"+
"&_busy_timeout=5000"+
"&_foreign_keys=on"+
"&_txlock=immediate")
重点看最后一行:_txlock=immediate 把申请写锁的时机提前到了事务开始。
WAL,即预写日志模式,会先把新写入放到 -wal 文件,之后再合并进主数据库,让读取和写入更容易并行。Agent 查询构建状态时,就不必总与另一个部署请求互相等待。但 WAL 没有取消单写入者限制,_busy_timeout=5000 只是让竞争写锁的连接最多等待 5 秒。
为什么还要提前申请写锁?普通事务可能先读取,等真正写入时才升级锁。如果期间其他连接已经提交,锁升级可能直接失败,等待超时也救不了。作者称,仓库为这个具体场景写了测试。
_foreign_keys=on 则开启外键约束。每个数据库最多开放 4 个连接,也是控制并发的一部分。
这些设置能解释方案为什么合理,却不能证明任意负载都适用。作者明确承认系统围绕一台机器设计,多节点仍是已知缺口。原文也没有给出吞吐量、延迟或与 Postgres 的对照测试。「4 个连接足够」是作者对当前单机场景的判断。
在线备份要读懂 WAL,恢复还要找回密钥和镜像
运行中的数据库旁会出现 ah.db-wal 和 ah.db-shm。最近提交的内容可能还停留在 WAL,直接 cp ah.db 可能遗漏新数据,也可能拿到不一致的文件。
ah backup 使用 SQLite 的 VACUUM INTO,通过数据库引擎读取包含 WAL 的一致视图,生成独立副本。作者采用只读连接,避免备份占用运行服务的写锁。
执行入口是 sudo -u ah /usr/local/bin/ah backup /var/lib/ah/ah.db。它会创建仅所有者可读的备份目录,磁盘使用率达到 80% 时警告,达到或超过 90% 时停止;随后以 5 分钟超时生成临时副本,再 gzip 压缩。
成品名类似 ah-20261027-091700.db.gz,计量库则是 ah-metering-20261027-091700.db.gz。压缩过程先写 .tmp,完成后再重命名,避免半成品冒充可用备份;两类备份分别保留最新 10 份。每小时运行可以使用 cron:17 * * * * ah /usr/local/bin/ah backup /var/lib/ah/ah.db。
但备份目录还不是完整恢复包。
master.key 不会进入备份,必须另行保存。拿到数据库和密钥的人可以解密其中的秘密,所以作者建议分开保管。执行 ah rotate-key 后,旧备份仍然依赖旧密钥,密钥保留期限必须覆盖对应备份。
副本还要离开原机器。作者给 Agent 的任务包括:每小时用 rsync 同步 /var/lib/ah/backups/,轮换后更新密钥保管记录,最新备份超过 2 小时就告警。如果不能接受小时级的数据丢失窗口,仓库提供可选 Litestream 配置 deploy/litestream.yml,以及 docs/ops/ 下的操作手册,将两个数据库的变化持续传到 S3 兼容存储;这项服务需要自行运行,ah 不负责安装管理。
恢复时没有 ah restore 命令。先在新服务器安装 ah,复制 /etc/default/ah,放好备份和对应密钥,再以 root 执行:
systemctl stop ah
cd /var/lib/ah
mkdir -p pre-restore
mv ah.db* ah-metering.db* pre-restore/
TS=20261027-091700
gunzip -c backups/ah-$TS.db.gz > ah.db
gunzip -c backups/ah-metering-$TS.db.gz \
> ah-metering.db
install -m 600 -o ah -g ah \
~/master.key master.key
chown ah:ah ah.db ah-metering.db
chmod 600 ah.db ah-metering.db
systemctl start ah
最容易漏的是 mv ah.db* ah-metering.db* pre-restore/:它把旧数据库连同 -wal、-shm 一并移走,防止残留 WAL 干扰恢复后的文件。没有计量备份时,可以不恢复计量库,程序会创建空库,但用量历史会丢失。
启动后,可用 curl -s -H 'X-Forwarded-Proto: https' http://127.0.0.1:8080/v1/system/health 检查健康状态。本地请求需要这个头,因为 API 要求 HTTPS。密钥错误时,启动校验会失败,journalctl -u ah 中会出现 wrong master key?,而不是等到所有 API key 都被拒绝才暴露问题。
应用仍需处理。名为 reconciler 的协调循环每隔 30 秒核对数据库与 Docker;记录显示运行、实际却没有容器的应用,会被标记为 crashed,原因是 container not found by reconciler,不会自动重启。
此时可执行 ahc service redeploy my-api,再用 ahc status 查看状态。重新部署依赖已有镜像;如果镜像只存在于旧服务器的本地 registry,新机器拿不到,就需要从 git 重新构建。最后还要打开一个使用数据库密码等秘密的应用,验证密钥和数据库确实配套。
我的判断
我认同作者把讨论重点放在恢复能力上。对于单机控制面,减少依赖、明确数据位置、把恢复步骤写成能演练的操作,确实比笼统争论 SQLite 是否「专业」更有价值。
但「几分钟恢复」有条件。原文没有提供完整计时,也没有说明应用数量、镜像大小、业务数据库容量和网络传输耗时。控制面文件恢复得快,不代表业务也能同时恢复。SQLite 与 Postgres 的性能取舍,原文同样缺少对照数据,这部分只能看作经验之谈。
作者认为 Agent 能在一个下午补齐备份、同步和监控组件,这也是观点,不能据此认定它已经具备大型托管平台同等的冗余能力。脚本有人生成之后,仍要验证告警会不会响、旧密钥能不能找到、业务卷能不能恢复。
最有说服力的交付物,是一次在新机器上完成的恢复演练。
能拿来做什么
- 给自托管的前端或 Node 应用列一张恢复清单,分别标明平台状态、业务数据库、加密密钥、镜像和启动配置的位置,确认每一项都有获取办法。
- 如果自己的工具也用 SQLite WAL,检查定时任务是否仍在直接复制主数据库文件,优先采用数据库支持的一致性备份方式,并把副本同步到另一台机器。
- 让 AI 编程助手生成备份定时任务、同步脚本和超时告警后,逐项审阅实际路径、运行身份、保留数量及失败处理,再安排一次恢复验证。
- 用临时服务器演练从备份启动一个依赖秘密配置的应用,记录控制面恢复与应用真正可用分别花了多久,把镜像缺失、密钥不匹配等问题提前暴露出来。