Back up the database automatically on every compose up
A one-shot backup service runs before the app starts (depends_on service_completed_successfully; compose re-runs exited one-shots on every up), so the plain docker compose pull + up -d update flow always snapshots the database first. Uses SQLite's online-backup API via better-sqlite3 from the app image itself - safe against the live WAL database, no downtime, no extra tooling. Backups land in ./backups (newest 14 kept); a failed backup blocks the app from starting rather than updating without a safety net.
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
node_modules/
|
node_modules/
|
||||||
/data/
|
/data/
|
||||||
|
/backups/
|
||||||
*.db
|
*.db
|
||||||
*.sqlite
|
*.sqlite
|
||||||
.env
|
.env
|
||||||
|
|||||||
@@ -1,6 +1,38 @@
|
|||||||
services:
|
services:
|
||||||
|
# One-shot pre-start backup. `docker compose up -d` re-runs this every time
|
||||||
|
# (a stopped `restart: "no"` service is started again on each up), and
|
||||||
|
# tripplan waits for it to complete successfully — so the plain
|
||||||
|
# `docker compose pull && docker compose up -d` update flow always snapshots
|
||||||
|
# the database before the new version boots. Uses SQLite's online-backup API
|
||||||
|
# (via better-sqlite3, already in the app image), which is safe against a
|
||||||
|
# live WAL database — no downtime, no torn copies. Backups land in
|
||||||
|
# ./backups next to this file; the newest 14 are kept.
|
||||||
|
# Restore: docker compose stop && cp backups/trip-plan-<stamp>.db data/trip-plan.db
|
||||||
|
# && rm -f data/trip-plan.db-wal data/trip-plan.db-shm && docker compose up -d
|
||||||
|
backup:
|
||||||
|
build: .
|
||||||
|
entrypoint: ["/bin/sh", "-c"]
|
||||||
|
command:
|
||||||
|
- |
|
||||||
|
set -e
|
||||||
|
DB=/app/data/trip-plan.db
|
||||||
|
if [ ! -f "$$DB" ]; then echo "backup: no database yet, nothing to back up"; exit 0; fi
|
||||||
|
mkdir -p /backups
|
||||||
|
STAMP=$$(date +%Y%m%d-%H%M%S)
|
||||||
|
node -e "require('better-sqlite3')('$$DB',{readonly:true}).backup('/backups/trip-plan-'+process.argv[1]+'.db').then(()=>{console.log('backup: /backups/trip-plan-'+process.argv[1]+'.db written');process.exit(0)}).catch((e)=>{console.error('backup failed:',e.message);process.exit(1)})" "$$STAMP"
|
||||||
|
ls -1t /backups/trip-plan-*.db 2>/dev/null | tail -n +15 | xargs -r rm -f
|
||||||
|
volumes:
|
||||||
|
- ./data:/app/data
|
||||||
|
- ./backups:/backups
|
||||||
|
restart: "no"
|
||||||
|
|
||||||
tripplan:
|
tripplan:
|
||||||
build: .
|
build: .
|
||||||
|
# If the backup fails, the (old or new) app deliberately does not start —
|
||||||
|
# fix the backup problem (usually disk space) rather than removing this.
|
||||||
|
depends_on:
|
||||||
|
backup:
|
||||||
|
condition: service_completed_successfully
|
||||||
ports:
|
ports:
|
||||||
- "3000:3000"
|
- "3000:3000"
|
||||||
volumes:
|
volumes:
|
||||||
|
|||||||
Reference in New Issue
Block a user