backupctl --用restic的备份脚本

目录

掐指一算,明天 codex 最后一张重置卡过期,宜蹬些代码。

Backupctl 是我在 2026-08-12 搞的另一个 VPS 备份方案,目前效果还不是很好。

V1 是纯 shell 实现;V2 是 shell 实现 install 脚本,Python 实现功能模块。

V2 搞了一个 CLI 的功能,通过 backupctl 控制备份和测试。现在 AI 还是擅长使用 CLI,整一个准没错。

当前方案

现在这套方案大致分成几层:

1
2
3
4
5
网络层:Tailscale
传输层:SFTP / HTTPS
备份层:Restic
调度层:systemd
通知层:Telegram

其中:

draw.io 画的备份流程(不太正确,不想修改了)

用 Restic 实现类似增量备份的效果:就是将文件创建一个逻辑快照,再将文件切成数据块,已经存在的相同数据块不会上传,仅仅上传每次备份不同的数据块。Restic 的设计文档也说明了快照、内容分块和去重机制。Restic 设计文档 增量备份与差异备份——数据备份策略之间的区别

备份目标包括 Backblaze B2 对象存储和 NAS。

缺点就是暂时不能备份数据库。数据库需要单独使用 mysqldump 或者其他命令导出,再使用 Restic 备份导出文件;否则直接备份正在运行的数据库文件,可能会出现一致性问题。

上一个备份方案则是使用 tar gzip 打包压缩,使用 rsync 远程传输,用 Tailscale 实现远程组网,NAS 备份。缺点就是没有加密,而且是全量备份。

遇到的一些问题

有一些开发问题,主要还是和 AI 协作的问题。

当我鞭策 AI 写出新的脚本的时候,其实效果不太好,即使我用的是 sol xhig + plan 模式。

说到这个 plan 模式,我发现现在其实不需要使用者自己去设计 herdness ,我用的 oh-my-pi 和 codex 都有 plan 模式。 但是这个 plan 模式也不是万能的,plan 模式我都是给的强推理的强力模型(看别人都是用强力模型规划,普通模型写代码)。但是做出来的效果也不太符合我的预期,做出来之后也没有变得简单和更能掌握这个项目,用 plan 模式的时候应该用 grill-me 这个 skill 拷打一下才行。superpower 已经过气了

  • 一来是因为 APT 的 Restic 版本太过落后,一些性能问题、SFTP 的问题还没修好;
  • 二来是 Tailscale 组网的端对端延迟过高,在做核对关联的时候需要反复确认。每次核对确认都要走 200 ms 的 RTT ,网络开销自然高,用的时间也多。
  • 三来是为了继承旧的备份脚本使用 shell 的传统,新的方案也全是用 shell,结果整得挺复杂的,给我带来挺大理解负担。我一直觉得,如果某种工具过于强大而全面,还不如去用符合自己需求的简单工具,KISS 嘛。第一版 shell 的备份脚本搞好之后,还是让 AI 用 Python 重构了,希望代码量少一些,结果代码量其实差不多;
  • 第四点就是写第一个版本的时候,又想试一下 subagent 的能力,又想省一些 terra max 档位,结果就是 terra 的能力不太行,干活时一直被 PowerShell 转义绊倒。像 sol 就能在我不提醒的情况下,用 base 64 编码执行 ssh -o 命令,避免转义问题。还有就是 subagent 干活真的很慢。真要用,应该开几个 subagent 分工干不同功能模块的活。结果就是开 subagent,terra 在吭哧吭哧干活,sol 在旁边看戏,偶尔指导两句;
  • V1 -> V2 重构的效果也不是很好。现在想起来,应该在旧的对话里面写一份交接文档,然后开一个新的对话开始重构。因为过度信任 sol xhigh 的能力,以为它能看出整个项目的功能点,结果最后给我遗漏了 Telegram 通知的功能(如果是一个大项目不敢想会出多少 BUG);
  • 还有一个就是让提交到 GitHub 个人仓库里面,直接把我的 VPS 信息、配置目录也上传上去了。虽然没有把 .env 上传上去,但总感觉有些异味;
  • 一开始在我问为什么 SFTP + Tailscale 会检查耗时、什么耗时的时候,只给我结论。这个是唯一让我觉得红温的;
  • 已经配置了 draw.io MCP ,在收尾的时候,让画一份 draw.io 的草图,居然没有触发 MCP,而是打算用 XML 给我画草图。还是我说明了用 draw.io,才给我用 XML。画 draw.io 的时候还在用 /n 作为换行符;
  • 还有一个问题是,codex 像一个过度谨慎的人,不愿意犯错(也可能是我本能不希望 codex 犯错)。之前使用的时候就有感觉,codex 喜欢简单的东西复杂化,过度规划2026-07-25 发现的做 PPT 过度规划校验的问题。完全可以先起一个 demo ,然后再去扩展。尤其是现在用上 plan 模式之后,感觉更加孤僻了(完全不听劝埋头苦干)。

想睡觉了就先写这么多了,明天再研究一下能不能用 skill 或者全局提示词改一下 codex 的行为。

文件锁

框架锁: /run/lock/backup-framework.lock,防止同时运行备份行为。 外部锁:/run/lock/vaultwarden-backup.lock ,防止新旧两个框架同时运行。

1
2
# 查找锁的持有进程
lslocks | grep something

在 Linux 中,“一切皆文件”(包括普通文件、目录、网络套接字、设备、管道等)。所以其实也是能用 lsof List Open Files(列出打开的文件) 查看当前系统中所有进程正在使用的资源。

1
2
3
4
5
6
7
8
# 查找哪个进程占用文件
lsof /var/log/syslog

# 查找占用80端口的进程
lsof -i :80

# 查看某个进程打开了什么文件
lsof -p 1234

Systemctl timer

一般来说计时器控制服务的定时运行,当出现一些异常状况比如恰好前面停电,而开机在计时器预定时间之后,就会立刻执行一次服务的执行。在学校晚上断电的时候就很有用

定时任务归 systemd 管,也可以用 journalctl 查看。

服务文件: /etc/systemd/system/backup-db.service

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
[Unit]
Description=Backup MySQL database
After=network.target

[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup-mysql.sh
Restart=on-failure
RestartSec=30

[Install]
WantedBy=multi-user.target

计时器文件:/etc/systemd/system/backup-db.timer

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
[Unit]
Description=Run backup-db service daily at 2 AM

[Timer]
# 每天凌晨 2 点执行
OnCalendar=*-*-* 02:00:00
# 如果系统关机错过了,开机后立即补执行
Persistent=true
# 允许误差 1 分钟,避免多个任务同时触发
RandomizedDelaySec=60

[Install]
WantedBy=timers.target

启动:

1
2
systemctl start mytask.timer
systemctl enable mytask.timer
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
systemctl list-timers
NEXT                        LEFT          LAST                        PASSED       UNIT                         ACTIVATES
Thu 2026-08-13 00:00:00 CST 43min left    -                           -            dpkg-db-backup.timer         dpkg-db-backup.service
Thu 2026-08-13 00:00:00 CST 43min left    Wed 2026-08-12 06:00:42 CST 17h ago      logrotate.timer              logrotate.service
Thu 2026-08-13 03:30:00 CST 4h 13min left Wed 2026-08-12 19:19:51 CST 3h 57min ago backup-all.timer             backup-all.service
Thu 2026-08-13 06:08:41 CST 6h left       Wed 2026-08-12 12:10:07 CST 11h ago      apt-daily-upgrade.timer      apt-daily-upgrade.service
Thu 2026-08-13 11:09:58 CST 11h left      Wed 2026-08-12 11:09:58 CST 12h ago      systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service
Thu 2026-08-13 13:28:56 CST 14h left      Wed 2026-08-12 19:03:58 CST 4h 12min ago apt-daily.timer              apt-daily.service
Sun 2026-08-16 03:10:46 CST 3 days left   Sun 2026-08-09 09:10:04 CST 3 days ago   e2scrub_all.timer            e2scrub_all.service
Mon 2026-08-17 00:57:40 CST 4 days left   Mon 2026-08-10 06:13:54 CST 2 days ago   fstrim.timer                 fstrim.service

journalctl -u apt-daily-upgrade.timer
Aug 12 10:54:32 [hostname] systemd[1]: [timer]: Deactivated successfully.
Aug 12 10:54:32 [hostname] systemd[1]: Stopped [timer] - Daily apt upgrade and clean activities.
-- Boot [boot-id] --
Aug 12 10:55:02 [hostname] systemd[1]: Started [timer] - Daily apt upgrade and clean activities.

本地备份

不能把钥匙放在保险柜里面,对于 restic 的密码来说,不能继续存在 vaultwarden 里面。

我也早就不想把把一些重要的入口密码交给云备份了。

可以考虑用下面的工具存放,文件都是 .kdbx 格式。 一份放在手机,一份放在电脑,感觉就差不多了。 keepassxc 支持 Windows、Linux、macOS。 KeePassDX 支持 Android。

TODO

rest-server 似乎能解决 restic 使用 SFTP 的网络开销问题,改天搭建试试看。

毕竟我用 NAS 一个主要目的是跑备份,能方便备份也不错。