feat(ops): back up by default, somewhere the operator can see (#414) #446
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/414-backups-on-by-default"
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?
First half of #414. Addresses the part a self-hoster feels immediately; the off-host half is deliberately separate, for a reason set out below.
What changed
Enabled by default. Backups shipped off, and production ran for months with
backup_logsempty and not one scheduled dump ever taken — every dump that existed was made by hand, mid-incident, by someone who already knew the feature was there. Off is the wrong default for the one feature whose whole purpose is to be present before anyone thinks to want it. An explicitly storedenabled: falseis still honoured; this changes the default, not a decision anyone made.Named volume →
./backupsbind mount. The old location lives under/var/lib/docker/volumes— exactly the path people don't include when they back up a machine. Operators back up/homeand their project directories, so the dumps sat on the same disk and outside whatever backup habit already existed, findable only viadocker volume inspect../backupssits beside the compose file and can be handed torclone,resticorrsyncby someone who knows nothing about Docker.Ownership handled, so there's nothing to prepare. Docker creates a missing bind-mount source as root, and the app runs as
appuser(uid 999) — verified on prod. Without a fix, the first scheduled backup on a fresh install fails onEACCES, silently, forever. Themigrateone-shot now does it: it already runs before backend and worker, so the long-running services keep their non-root posture rather than starting as root to chown a directory. Alembic uses its own database credentials, so root in a throwaway container grants nothing extra.Why the audio volume isn't here
The issue asks for "an equivalent backup path for the audio volume", and I started building it before concluding it would be close to useless in this form.
Copying audio into
./backupsputs a second copy on the same disk, doubling consumption — potentially by gigabytes per session — while protecting against nothing the database dump doesn't already cover. The failure audio needs protecting from is losing the host, and only an off-host copy addresses that.So audio coverage isn't a peer of the database dump; it's part of the off-host work, and I'd rather ship that once than ship a local copy that costs disk and buys nothing. #414 stays open for it.
What this explicitly does not do
Stated in
docs/OPERATIONS.mdas a table rather than left to be discovered — the media volume, the audio volume, and any off-host copy are all still uncovered. A backup on the same disk survives a bad migration, not a dead machine.That honesty matters here: #429 was a backup that reported success and could not be restored, and the lesson is that a backup you believe in but haven't checked is worse than none.
Upgrading
Existing dumps stay in the orphaned
quest-board_backupsvolume. Nothing moves or deletes them — relocating somebody's only backups unasked is not something a deploy should do quietly.OPERATIONS.mdhas the copy command and says to verify the files before removing the old volume.Verification
test_admin_get_backup_config_defaultfails on a plainassert False.docker compose configvalidates, with all three services (migrate,backend,worker) resolving./backups→/app/backupsandmigrateas root.🤖 Generated with Claude Code
b3a4ed49daaac322b693