restic Backup Script
Assemble the daily script from the discovery artifacts. It dumps databases first, backs up the discovered paths, tolerates partial-read exit code 3, then applies retention. A ready-to-adapt template is in references/restic-backup.sh.
Inputs (from discover-backup-sources)
/etc/restic/backup-paths.txt— include paths/etc/restic/excludes.txt— exclude globs/etc/restic/databases.tsv— DB containers + dump commands
The script: /usr/local/sbin/restic-backup.sh (mode 700, root)
Logical order — get it right or you back up inconsistent data:
- Set a clean environment. Absolute
PATH(cron/systemd have a minimal one), thenset -a; . /root/.restic/r2.env; set +a. - Dump databases first, into
/var/backups/restic-dumps/(mode 700). Usedocker exec -i— never-t/-it(no TTY under cron/systemd → empty/hung dump). See the dump matrix indiscover-backup-sources/references/database-dumps.md. - Back up, tolerating exit code 3:
set +e restic backup --tag daily \ --files-from /etc/restic/backup-paths.txt \ --exclude-file /etc/restic/excludes.txt \ --exclude-caches rc=$? set -e # 0 = ok, 3 = some files unreadable but snapshot created (NOT fatal) if [ "$rc" -ne 0 ] && [ "$rc" -ne 3 ]; then echo "restic backup failed: $rc" >&2; exit "$rc" fi - Retention:
restic forget --tag daily \ --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune - Log to
/var/log/restic-backup.log; optionally ping a healthcheck (seemonitoring).
Why exit code 3 must be tolerated
restic returns 3 when some source files couldn't be read (permissions, a file vanished mid-run) but a snapshot was still created. Under set -e an unguarded restic backup would abort the script before forget/prune and before the success ping — turning a healthy partial backup into a false failure. Guard it as shown. Only 1 (no snapshot), 10 (repo missing), 11 (locked), 12 (wrong password) are real failures.
Retention policy
--keep-daily 7 --keep-weekly 4 --keep-monthly 6 keeps a week of dailies, a month of weeklies, half a year of monthlies, then --prune reclaims space. Adjust to the user's recovery-window and budget. forget deletes snapshots; prune repacks and frees storage — keep them together (or run prune on its own less-frequent timer for very large repos).
Excludes
Start from the discovery excludes (/etc/restic/excludes.txt). --exclude-caches additionally skips any directory containing a CACHEDIR.TAG. Consider --exclude-larger-than 5G to skip stray huge files, and --one-file-system only deliberately (see gotchas).
Gotchas
-t/-itindocker execunder cron/systemd = the #1 silent failure (no TTY → empty dump). Always-ionly. Verify dumps are non-empty.set -evs exit 3 — wrap therestic backupcall as shown, or a partial-but-successful run aborts before pruning and alerting.--one-file-systemsilently skips data on separate mounts — Docker named volumes under/var/lib/docker/volumesand separately-mounted disks get dropped. Use it only when you truly want single-filesystem scope, and cross-check captured paths inverify-backup.- Staging dir double-count —
/var/backups/restic-dumps/must not also be matched by another project's include glob. - Permissions — script
root:rootmode700; it reads secret files and runsdocker/dump tools. - Missing paths — build the include list skipping nonexistent entries (
[ -e "$p" ]) so a removed project doesn't fail the run. The--files-fromfile should only list paths that exist; regenerate it viadiscover-backup-sourceswhen projects change.
What this skill does NOT cover
- Scheduling the script →
scheduling - The first verified run + enabling →
verify-backup - Alerting / freshness →
monitoring