Backup sidecar: dump, volume archive, prune, and status #83
Labels
No Label
area:auth
area:docs
area:export
area:ops
area:storage
area:supply-chain
auth
backend
blocked
collab
deployment
docs
effort:L
effort:M
effort:S
frontend
plugins
qa
vs-nfd
vs-nfd:blocker
No Milestone
No project
No Assignees
1 Participants
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: stwaidele/dorfteich#83
Loading…
Reference in New Issue
Block a user
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?
Context
ADR 0015 defines the nightly backup set; the sidecar makes it real for every stage and self-hoster.
Scope
Build the
dorfteich-backupsidecar image: nightly (cron env-configurable)pg_dump -Fc+ uploads/plugins volume tar with shared backup id, prune by retention (env: 30 d default, 7 d Test/Int),status.json(last run, outcome, sizes, backup id) on the backups volume, failure mail via the instance SMTP (direct nodemailer call — the API may be the thing that is broken), restore helper script (restore.sh <backup-id>) implementing the runbook steps; compose integration for all stages.Acceptance criteria
Technical notes
Dependencies
Depends on #6.
Size: ~2 days
Conventions: English code/comments, clear human-readable code, no hard-coded UI strings (ADR 0012, add
deanden), permission checks only via the shared guard (docs/architecture/permissions.md). Read the referenced ADRs before starting.Implemented in
8dbff86(pipeline green, 8/8 contexts; provisioned and drilled on Test + Int).New workspace app
apps/backup(@dorfteich/backup) — thedorfteich-backupsidecar image (ADR 0015):BACKUP_TIME/TZenv; no cron dependency — one re-armedsetTimeoutto the next HH:MM):pg_dump -Fcfirst, then one tar of the uploads + plugins volumes, both under a shared backup idYYYYMMDD-HHMMSS(UTC). Artifacts are staged as.partialand renamed on completion, so a crash never leaves something that looks restorable.BACKUP_RETENTION_DAYS(default 30; Test/Int set 7 per ADR 0015) — and never the newest complete set, even when expired: after long downtime the last restorable set survives until a new success replaces it.status.jsonon the newbackupsvolume (atomic write):schemaVersion,lastRun(id, timestamps, duration, outcome, error, sizes) andlastSuccesscarried across failed runs — the consumer contract for the #85 freshness check and the #86 admin card.mailsi18n catalog (de + en,BACKUP_MAIL_LOCALE), recipientBACKUP_MAIL_TO(unset = log only). SMTP resolves exactly like the api: explicit env wins, the wizard-written secret store fills gaps, empty strings count as unset — the pure secret-store helpers moved to@dorfteich/sharedfor that (api re-exports them, import paths unchanged).deploy/backup/restore.sh <backup-id>automates the runbook (stop web/api/collab — db stays up —,pg_restore --clean --if-exists+ volume untar via the sidecar image,docker compose up -d, wait for/readyz). On-demand backup:docker compose run --rm -e BACKUP_RUN_ONCE=1 backup(exit code = outcome, the hook for #86's manual trigger).Acceptance criteria
runner.test.tsdrives 30 simulated nights through an injected clock against a real temp filesystem; the realpg_dump/tarpath verified locally against a fixture DB and live on both stages (Test dump 61 KB, Int 247 KB).backup-set.test.ts, incl. "newer incomplete set does not shield it") plus the 60-days-downtime scenario inrunner.test.ts.fixture-user's display name,./restore.sh 20260711-154136, value reverted,/readyzfully green. (The recurring automated monthly drill is #87.)status.jsonoutcomefailedwithlastSuccesspreserved, mail delivered); covered in tests including "alert transport down never breaks the run".CI/CD: backup image added to the PR image-build job, the CD build-push (
:sha+:test) and the Int retag. Stages: composes replaced (backups*.bak-pre83),.env+TZ=Europe/Berlin,BACKUP_RETENTION_DAYS=7,BACKUP_MAIL_TO/BACKUP_MAIL_LOCALE;restore.shdeployed to both stage directories..env.example, operations.md and deployment.md updated.Out of scope, as planned: BASEL mirror (#84), readyz freshness degradation (#85), admin panel card/trigger + dump download (#86), automated monthly drill (#87).