编程 append-only 仓库里 keep-weekly 会删掉合法快照:restic forget 的保留策略与 --keep-within

2026-10-27 00:03:59

append-only 仓库里 keep-weekly 会删掉合法快照:restic forget 的保留策略与 --keep-within

restic 0.19.1。删除备份不是一条命令的事:forget 把快照从列表里摘掉,prune 回收只被这些快照引用的数据。两步分开,各有各的坑。

项目地址:restic.net / github.com/restic/restic

两阶段:forget 摘快照,prune 回收数据

手动删一个快照:

restic -r /srv/restic-repo snapshots
restic -r /srv/restic-repo forget bdbd3439

此时快照引用的数据仍留在仓库里。要清掉无引用数据,必须再跑 prune:

restic -r /srv/restic-repo prune

输出会依次经历:加载所有快照、加载索引、查找仍在使用中的数据、搜索使用中的 pack、收集待删除与待重打包的 pack,随后打印 to repack / to delete / total prune / remaining / unused size after prune,再 repacking packs、rebuilding index、删除过时索引文件、removing 3 old packs。以上为文档示例输出,未实测。

forget 的 --prune 选项把第二步自动化,只在确实删掉快照时才触发 prune:

restic forget --keep-last 1 --prune

prune 耗时取决于快照数量和数据量。执行期间仓库被加锁,备份无法完成,要留出能跑完的时间窗口,别和常规备份撞车。prune 之后建议跑 restic check,仓库内部数据结构一旦损坏能及时告警。

策略选项

  • --keep-last n:保留最近 n 个快照。
  • --keep-hourly n:最近 n 个有快照的小时里,每小时只留最新一个。
  • --keep-daily n、--keep-weekly n、--keep-monthly n、--keep-yearly n:同样的规则,按天/周/月/年。
  • --keep-tag:保留至少匹配一个 tag 列表的快照。每写一次定义一份列表,列表内逗号分隔的标签是 AND;多个 --keep-tag 之间是 OR。如果某个分组内所有快照都因不满足 tag 策略而被删,forget 会以错误退出。
  • --keep-within duration:保留时间戳落在「最新快照往前这段时间」内的所有快照,duration 形如 2y5m7d3h。
  • --keep-within-hourly / -daily / -weekly / -monthly / -yearly:同样的思路,按小时/天/周/月/年。

日历相关选项走自然时间边界,不相对 forget 运行时刻计算:周为周一 00:00 到周日 23:59,天为 00:00 到 23:59,小时为 :00 到 :59。只统计有快照的小时/天/周。值 unlimited 解释为永远。

duration 相关选项会忽略时间戳在未来的快照(相对于 forget 运行时刻),这些快照不会被删除。

如果快照数量不足以让每个 duration 选项都保留一个,最旧的快照会额外保留,并在输出中标为 oldest。

--keep-tag '' 只匹配没有标签的快照。

分组语义

带策略运行 forget 时,restic 先加载全部快照,按主机名和路径分组。分组方式用 --group-by 指定,例如 --group-by paths,tags。策略对每个分组单独生效——这是防止误删不相关备份集的安全特性。用 --group-by ''(空值)关闭分组。backup 命令通常设成同一个值,保持口径一致。

用 --host 或 --tag 收窄策略范围。多个 --tag 之间是 OR,逗号是 AND:

restic forget --tag foo --keep-last 1
restic forget --tag foo --tag bar --keep-last 1
restic forget --tag foo,bar --keep-last 1
restic forget --tag '' --keep-last 1

两个策略例子

每天备份、连续 100 年:

restic forget --keep-daily 7 --keep-weekly 5 --keep-monthly 12 --keep-yearly 75

保留最近 7 个 daily,外加 4 个周最后一天(7 个 daily 里已包含 1 个 weekly)、12 或 11 个月最后一天、75 或 74 年最后一天,其余全部删除。

备份时间不规则时改用 within 系列:

restic forget --keep-within-daily 7d --keep-within-weekly 1m --keep-within-monthly 1y --keep-within-yearly 75y

注意 1w 不是被识别的 duration,要写 7d。

所有 --keep-* 选项一起参与评估,快照只要匹配其中任意一个就保留,结果是 OR。

举个 11 个快照跨两周、缺最后一个周三的例子:

restic forget --keep-daily 5 --dry-run

只保留最近五个「有快照的日子」每天一个。因为周三备份缺失,前一个周五的快照会被保留。换成 --keep-within-daily 7d,则是最近七天内每天最多留一个,第一周的备份一个都不剩。

删除全部快照

restic 拒绝空策略。--keep-last 0 一个快照都不删。确实要全删,先用 --keep-last 1,再按 ID 手动删最后一个。从 0.17.0 起,--unsafe-allow-remove-all 可以删掉特定 host、tag 或 path 下的所有快照,但必须配合过滤器:

restic forget --tag example --unsafe-allow-remove-all

append-only 模式下的投毒场景

TL;DR:在 append-only 仓库里删快照,专门用 --keep-within。

append-only 模式下仓库只可写、可读,删除和覆盖被拒绝。rest-server 支持 append-only,其他后端可以在中间放一层 rclone 实现。forget 和 prune 需要完整的读、写、删权限,攻击者一旦拿到这些权限,append-only 保护就失效了。推荐给 forget/prune/维护单独准备一台加固过的客户端。

即便如此,能往仓库里塞垃圾快照的攻击者,仍可以把快照列表推到某个状态,让不知情的管理员用某些 --keep-* 选项跑 forget 时把合法快照删光。例子:策略保留三个 weekly 快照;攻击者给最近三周各加一个空快照,--time 设得比现有快照稍新、但仍落在目标周内;下一次 forget 每周只留最新那个,合法快照被删。不跑 prune 的话恢复很麻烦,还会丢一部分元数据。

用 --keep-within 可以避开这个局面:它同时保留攻击者的快照和合法快照;在系统时间正确的前提下,管理员能注意到快照数比平时多,或时间戳可疑。

这个防护受 duration 范围限制:如果 forget --keep-within 7d 在最后一次好快照之后 8 天才运行,攻击者仍能利用这个窗口删掉全部合法快照。

prune 的重打包与 --max-unused

prune 扫描所有快照和目录,判断哪些数据仍在使用。对每个文件分成三种:完全使用、部分使用、完全未使用。完全未使用的标记删除;完全使用的保留;部分使用的按选项保留或标记重打包。重打包要把文件从仓库存储下载、再把需要的数据重新上传,远程仓库上非常耗时。之后才执行重打包、修改索引、删除过时文件。

  • --max-unused:允许的未使用数据上限。可写绝对大小(200M)、相对仓库总大小的百分比(10%)或 unlimited(部分使用的文件不设限,元数据仍会重打包)。默认 5%。restic 会尽量少重打包,同时保证不超过这个上限。
  • --max-repack-size:限制待重打包文件的总大小。prune 先把所有重打包文件写好、最后才删过时文件,所以预计要重打包很多、又担心空间不够时有用。
  • --repack-cacheable-only:只重打包包含元数据且存于缓存的文件;速度快,但未使用数据可能超过 --max-unused。默认 false。
  • --repack-smaller-than:重打包所有小于该大小的 packfile,从更小的 --pack-size 压缩。
  • --dry-run、--verbose。

磁盘满时的救援

仓库膨胀到填满磁盘或配额时,prune 可能腾不出空间。prune 的设计是任何中断都让仓库保持可用,代价是需要临时空间。多数情况下:

restic prune --max-repack-size 0

0.13.0 之前的版本用 --max-repack-size 1。这条路径的前提是此前已经用 forget 删掉若干快照,否则没有可回收的数据。

复制全文 生成海报 运维实战 restic 备份恢复 prune Linux运维

推荐文章

程序员茄子在线接单