# backupctl --用restic的备份脚本

掐指一算，明天 codex 最后一张重置卡过期，宜蹬些代码。
<!--more-->
Backupctl 是我在 2026-08-12 搞的另一个 VPS 备份方案，目前效果还不是很好。

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

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

## 当前方案

现在这套方案大致分成几层：
```
网络层：Tailscale
传输层：SFTP / HTTPS
备份层：Restic
调度层：systemd
通知层：Telegram
```

其中：
- [Tailscale](https://tailscale.com/docs) 负责远程组网；
- [SFTP](https://aws.amazon.com/cn/what-is/sftp/) 负责 NAS 方向的远程传输；
- [Restic](https://github.com/restic/restic) 负责备份、加密、快照和去重；
- [systemd timer](https://wiki.archlinuxcn.org/wiki/Systemd/%E5%AE%9A%E6%97%B6%E5%99%A8) 负责定时调度；
- [Telegram Bot API](https://core.telegram.org/bots/api) 负责通知。

![draw.io 画的备份流程（不太正确，不想修改了）](https://1mg.1144514.xyz/2026/08/20260812235219252.webp)


用 Restic 实现类似增量备份的效果：就是将文件创建一个逻辑快照，再将文件切成数据块，已经存在的相同数据块不会上传，仅仅上传每次备份不同的数据块。Restic 的设计文档也说明了快照、内容分块和去重机制。[Restic 设计文档](https://github.com/restic/restic/blob/master/doc/design.rst)  [增量备份与差异备份——数据备份策略之间的区别](https://aws.amazon.com/cn/compare/the-difference-between-incremental-differential-and-other-backups/)

备份目标包括 [Backblaze B2](https://www.backblaze.com/docs) 对象存储和 NAS。

缺点就是暂时不能备份数据库。数据库需要单独使用 [`mysqldump`](https://dev.mysql.com/doc/refman/8.4/en/mysqldump.html) 或者其他命令导出，再使用 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` ，防止新旧两个框架同时运行。

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

在 Linux 中，“一切皆文件”（包括普通文件、目录、网络套接字、设备、管道等）。所以其实也是能用 `lsof`  List Open Files（列出打开的文件） 查看当前系统中所有进程正在使用的资源。
```
# 查找哪个进程占用文件
lsof /var/log/syslog

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

# 查看某个进程打开了什么文件
lsof -p 1234
```
## Systemctl timer
一般来说计时器控制服务的定时运行，当出现一些异常状况比如恰好前面停电，而开机在计时器预定时间之后，就会立刻执行一次服务的执行。~~在学校晚上断电的时候就很有用~~

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

服务文件： `/etc/systemd/system/backup-db.service `
```
[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`
```
[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
```
启动：
```
systemctl start mytask.timer
systemctl enable mytask.timer
```

```bash
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](https://github.com/keepassxreboot/keepassxc) 支持 Windows、Linux、macOS。
[KeePassDX](https://github.com/Kunzisoft/KeePassDX) 支持 Android。

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

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


---

> 作者: Chifan  
> URL: https://eo.chifan.de/posts/backupctl/  

