Integrate restic as a tfstate backup #41
Labels
No labels
blocked
bug
duplicate
effort
high
effort
low
effort
medium
effort
minimal
enhancement
help wanted
invalid
pr
feedback
pr
reviewing
question
status
backlog
status
document
status
done
status
implement
status
pending
status
planning
status
reviewing
waiting
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
tepichord/milner#41
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
restore. State is ~250 KB, so every snapshot is trivially small.
every non-reproducible file — state, gitignored tfvars, .env — in one snapshot. Verify with restic ls that you see .env and the .auto.tfvars files.
timer alone if you apply at odd hours.
scoped to the backup bucket only. R2 bucket locks are available if you want the offsite copy immutable against a stolen Cloudflare token.
Naina:
Overview
Add automated restic backups of the Milner repo tree (Terraform state, gitignored tfvars,
.envfiles) to two failure-domain-separated repositories — one on the/mntexternal drive, one outside it. Exclude.terraform/and.git/(both rebuildable/replicated). Backups run on a 30-minute systemd timer and explicitly after everyterraform apply. The restic password lives in a password manager and is never stored on disk.Technical Approach
Create
backup/directory with the following structure:Create
backup/excludes.txt— newline-separated patterns:Create
backup/milner-backup.sh— the core backup script:Environment variable
RESTIC_REPOSis an array set in a config file or exported by the caller.Create
backup/milner-init.sh— one-time repo initialization:Create
backup/milner-restore.sh— restore helper:Create
backup/post-apply-hook.sh— sourced by the user afterterraform apply:Create systemd user units in
backup/:restic-backup.service:restic-backup.timer:milner-backup.env(example, user creates on host):Add
restictoflake.nixdev shell — add tobuildInputs:Update
tests/test_static.py— add new scripts to shellcheck list:Update
OPERATIONS.md— add a new §7 section documenting the backup system:milner-init.sh)milner-backup.sh)milner-restore.sh)restic ls)passor password managerUpdate
AGENTS.md— addbackup/to the Repo Map table:Create
backup/README.md— documents the backup architecture, repo URLs, retention policy, and restore procedure.Implementation Details
Two repo locations (issue specifies "temporarily" for the second):
/mnt/backup/milner(external drive, mounted at/mnt)/home/<user>/milner-backup(on host disk, outside failure domain)restic init, password from password managerPassword handling:
RESTIC_PASSWORD_COMMANDcalls the password manager CLI (e.g.,pass show backups/milner). The password never touches disk. If the password manager requires interactive auth, the user unlocks it before running backups.1R2 bucket locks (optional immutability): Cloudflare R2 supports WORM bucket locks via dashboard or Wrangler CLI. Deferred to a follow-up — not blocking for initial integration.2
Dependencies:
restic(added to flake.nix), password manager (passor equivalent),bash(already required).Constraints:
shellcheck3set -e,cd "$(dirname "$0")"pattern4milner-backup.envis gitignored~/.config/systemd/user/), not system-levelOut-of-scope notes:
Acceptance Criteria
backup/excludes.txtexists and excludes.terraform/and.git/onlybackup/milner-backup.shrunsrestic backupwith--exclude-fileand--tag milner, thenrestic forgetwith--keep-hourly 6 --keep-daily 30 --keep-monthly 12 --prunebackup/milner-init.shaccepts repo URLs and runsrestic initbackup/milner-restore.shrunsrestic restorewith configurable snapshot and targetbackup/post-apply-hook.shtriggersmilner-backup.sh(sourced or exec'd)resticadded toflake.nixbuildInputsshellcheck(added totests/test_static.py)AGENTS.mdRepo Map includesbackup/directoryOPERATIONS.mdincludes §7 documenting backup/restore workflowbackup/README.mddocuments architecture, repo URLs, retention, restore procedureTesting Plan
python -m pytest tests/test_static.py -v— shellcheck passes for all new scriptspython -m pytest tests/test_static.py::test_shellcheck -v— specifically verify new scripts are lintedrestic init --repo /tmp/test-repothenbash backup/milner-backup.shwith a testRESTIC_REPOSarray pointing to/tmp/test-repo— verify snapshot created with correct tags and excludesrestic ls lateston test repo — confirm.envand.auto.tfvarsfiles are included,.terraform/and.git/are excludedbash backup/milner-restore.sh /tmp/test-repo latest --target /tmp/restore-test— verify files restored with correct pathsRelated Issues/PRs