Task A — make the container backup-capable + validated dump + mechanism fingerprint

SUPABASE_DB_URL is set in Coolify on the API service, and this WSL2 box can't
reach db.<ref>.supabase.co — so the backup runs INSIDE the API container, which
has the env + Supabase network. Made that real:
- Dockerfile: install postgresql-client (pg_dump/pg_restore) + rsync + bash in
  the runner image.
- backup-db.sh: added an integrity fingerprint on every run — pg_restore --list
  must parse the archive AND find ledger_entries, else the run FAILS + pages
  (stronger than the size check; catches a corrupt/structureless dump).
- BACKUP-RUNBOOK.md: rewritten for the container-exec reality — host cron does
  `docker exec <api> sh /app/scripts/backup-db.sh` (inherits env + network +
  pg_dump), or a Coolify Scheduled Task. Full restore-fingerprint steps included.

MECHANISM FINGERPRINT (run locally, docker + pg16): seeded a ledger_entries
table (137 rows) → ran backup-db.sh (dump + validate: 22 archive objects,
ledger_entries present) → pg_restore into a scratch DB → 137 rows restored,
exact match. The dump/validate/restore path is proven end-to-end; it's the same
pg_dump/pg_restore that run in the container against Supabase.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Kev
2026-07-18 23:08:31 -04:00
parent ccb9668f0c
commit c2c43cdc92
3 changed files with 50 additions and 22 deletions
+5 -2
View File
@@ -26,8 +26,11 @@ RUN npm ci --omit=dev --no-audit --no-fund
FROM node:20-alpine AS runner
WORKDIR /app
# curl is used by the /api/health smoke check (Coolify HEALTHCHECK).
RUN apk add --no-cache curl tini
# curl /api/health smoke check (Coolify HEALTHCHECK).
# postgresql-client (pg_dump/pg_restore) + rsync + bash — the nightly DB backup
# (scripts/backup-db.sh) runs INSIDE this container, where SUPABASE_DB_URL and
# the Supabase network are available. See docs/BACKUP-RUNBOOK.md.
RUN apk add --no-cache curl tini bash postgresql-client rsync
# PM2 is installed globally so the entrypoint can call `pm2 start` to
# boot all three pollers (NBA / WNBA / MLB) alongside the Express API.