feat(ops): scheduled Postgres backups via db-backup sidecar
All durable state (library, monitor/wanted lists, discovery, config, and the
encrypted credentials) lived only in the single postgres-data volume with no
backup — one volume loss = total loss. Adds a db-backup compose sidecar that
pg_dumps immediately on start then every BACKUP_INTERVAL_SECONDS (default daily),
keeping the last BACKUP_KEEP (default 7) custom-format dumps in ${BACKUP_DIR}.
Travels with the stack (no host cron), starts with `up -d`. .env.example
documents the knobs; README adds a "Backup & restore" section with pg_restore
steps and an off-box note. Verified live: sidecar wrote a valid 128K dump of the
real lyra DB (pg_restore -l lists all tables + _prisma_migrations).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -94,3 +94,33 @@ The web entrypoint runs `prisma migrate deploy` on every rebuild, so schema chan
|
||||
reach the live DB automatically. Enter Qobuz / Soulseek / Last.fm credentials in
|
||||
**Settings** (they persist across rebuilds), then follow an artist or request an album
|
||||
to feed The Floor.
|
||||
|
||||
## Backup & restore
|
||||
|
||||
**All** durable state — library metadata, monitored/wanted lists, discovery data,
|
||||
config, and the encrypted Qobuz/Soulseek/Last.fm credentials — lives in the single
|
||||
`postgres-data` volume. Losing that volume (disk failure, or a stray `down -v`) means
|
||||
losing everything, recoverable only by a full re-scan and re-entering every credential.
|
||||
|
||||
The `db-backup` sidecar guards against that. It runs `pg_dump` immediately on start and
|
||||
then every `BACKUP_INTERVAL_SECONDS` (default daily), keeping the last `BACKUP_KEEP`
|
||||
dumps (default 7) as compressed custom-format `.dump` files in `BACKUP_DIR`
|
||||
(default `./backups`). It starts automatically with `docker compose up -d`.
|
||||
|
||||
For **off-box** safety (a backup on the same disk doesn't survive a disk failure), point
|
||||
`BACKUP_DIR` at a NAS/synced path, or rsync `./backups` to another machine on a cron.
|
||||
|
||||
**Restore** into a fresh/empty database (stop `web`/`worker` first so nothing writes
|
||||
mid-restore):
|
||||
|
||||
```sh
|
||||
# with the stack up and db healthy:
|
||||
docker compose stop web worker
|
||||
docker compose cp ./backups/lyra-YYYYMMDD-HHMMSS.dump db:/tmp/restore.dump
|
||||
docker compose exec db pg_restore --clean --if-exists -U lyra -d lyra /tmp/restore.dump
|
||||
docker compose start web worker
|
||||
```
|
||||
|
||||
`--clean --if-exists` drops and recreates each object, so restoring over an existing DB
|
||||
is safe. To restore onto a brand-new host, bring up just `db` first
|
||||
(`docker compose up -d db`), restore, then `up -d --build` the rest.
|
||||
|
||||
Reference in New Issue
Block a user