Skip to content

Add weekly restic restore-verification drill (#139) - #188

Merged
nickvigilante merged 9 commits into
mainfrom
139-restic-verify-drill
Jul 3, 2026
Merged

Add weekly restic restore-verification drill (#139)#188
nickvigilante merged 9 commits into
mainfrom
139-restic-verify-drill

Conversation

@nickvigilante

Copy link
Copy Markdown
Owner

Summary

  • Adds a weekly restic-verify CronJob (Sun 05:00 ET, one hour after the Sunday prune):
    repo check with a rotating --read-data-subset=n/5 (100% pack coverage every 5 weeks, ~32 GiB egress/run ≈ $0.90/mo),
    per-tag snapshot freshness (< 48 h, all 8 tags),
    and a real restore of the 7 small tags (~1.8 GiB) with --verify plus sentinel-file assertions.
    syncthing-data (176 GiB) is deliberately not restored — covered by the rotating data read and the freshness check.
  • Adds a ResticVerifyStale dead-man alert (no successful drill in > 8 d),
    mirroring ResticBackupStale; no in-job Kuma/SMTP plumbing by design.
  • Every restic call waits up to 30 m for the repo lock (--retry-lock)
    so a Sunday prune overrun can't burn both job attempts.
  • Design spec and implementation plan committed under docs/superpowers/.
    Closes Backup: periodic restic restore-verification drill #139.

Notes for review:

  • The monitoring README's alert list was converted to a table while adding the new row
    (the original prose details were preserved);
    pipe-table rows are single physical lines — the documented exception to semantic line breaks.
  • Sentinel paths were verified against the live latest snapshots on 2026-07-01
    (restic ls latest --tag <tag>), including the Bitnami data/PG_VERSION nesting.
  • The verify CronJob is born Flux-owned via k8s/backup/kustomization.yaml
    (prune: false untouched; the backup/forget CronJobs stay manually managed).

Before merge

  • No secrets in the diff — the job references the ESO-managed restic-credentials Secret by name only.
  • New persistent dirs: none added (the drill restores into an emptyDir scratch).
  • Host-level changes: none.
  • SPOF impact: none — nothing new behind Authentik.
  • DNS: no new hostnames.
  • README walks through the drill end-to-end (k8s/backup/README.md, "Restore-verification drill").

Test plan

Pre-merge (done on this branch):

  • kubeconform (strict, CRD catalog): both manifests valid.
  • kubectl kustomize --load-restrictor LoadRestrictionsNone k8s/backup builds.
  • promtool check rules: SUCCESS, 4 rules.
  • shellcheck (-S info, SC3040 excluded for busybox pipefail) clean on the embedded script.

Post-merge (plan Task 4, on gandalf):

  • flux reconcile backup + homelab-monitoring; CronJob appears.
  • Manual green run: all four phase banners, All phases passed, success timestamp advances.
  • Red-path test: one-off pod with a bogus sentinel fails as expected.
  • Prometheus: ResticVerifyStale health=ok/inactive; staleness query returns minutes, not days.

🤖 Built with AI assistance.

Weekly restic-verify CronJob: structure check, rotating 1/5
read-data subset, per-tag freshness, and a real restore drill with
sentinel assertions for the seven small tags. Failure signal is a
single ResticVerifyStale dead-man rule via the #138 alerting stack.

Assisted-by: AI
Four tasks: verify CronJob (sentinel paths verified against live
snapshots), ResticVerifyStale rule, README runbook, and post-merge
live verification including a deliberate red-path test.

Assisted-by: AI
Four-phase drill: repo check with rotating 1/5 read-data subset
(full pack coverage every 5 weeks), per-tag snapshot freshness
(<48h), and a real restore of the seven small tags with sentinel
assertions. No in-job alerting plumbing -- the ResticVerifyStale
dead-man rule is the single failure signal.

Assisted-by: AI
Dead-man rule on kube_cronjob_status_last_successful_time for
restic-verify, mirroring ResticBackupStale: one expression catches
failed, crashed, and never-scheduled drills. 8d = weekly + 1d grace.

Assisted-by: AI
The Sunday 04:00 forget --prune holds an exclusive restic lock; a
prune overrun past 05:00 would instantly burn both drill attempts.
Wrap restic with --retry-lock 30m. Also correct the freshness
comment's offset-slop claim (looser, not stricter, for positive
offsets).

Assisted-by: AI
CI runs pre-commit over all files; the spec/plan commits missed the
mdformat pass locally.

Assisted-by: AI
@nickvigilante
nickvigilante merged commit f3aa635 into main Jul 3, 2026
2 checks passed
@nickvigilante
nickvigilante deleted the 139-restic-verify-drill branch July 3, 2026 02:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Backup: periodic restic restore-verification drill

1 participant