fix(ops): the hourly backup never ran, and an unset POSTGRES_USER crash-loops silently
**The backup that did not exist.** `.env.example` ships `EVENT_NAME=Max & Maria's Wedding` and the runbook tells you to `cp .env.example .env`. Compose's env_file parser reads that fine. POSIX `sh` does not: `. ./.env` aborts with "Unterminated quoted string" (verified, rc=2), and every variable defined after that line is left unset. The §10.2 cron script is `#!/bin/sh` + `set -eu` + `. ./.env`, so it exited before `pg_dump` — every hour, into a log nobody reads. The only automated backup of the one thing the runbook calls irreconstructible produced nothing, and §10.2's own "prove it works NOW" only catches it if `.env` is already final at that moment. The script reads NOTHING from `.env` — `POSTGRES_USER`/`POSTGRES_DB` are expanded inside the db container by the single-quoted `sh -c`. The source line was pure liability and is gone. `EVENT_NAME` is now double-quoted in `.env.example`, which both parsers read identically (verified), and the three interactive sourcing sites now read just `$DOMAIN` instead of sourcing the whole file. The verify step also proves the dump is a non-empty valid gzip containing tables, rather than that a file exists. Also fixes the script's `cd /root/eventsnap`, which contradicts §5's non-root deploy and §13's `~/eventsnap` — under a non-root deploy it failed the same way, silently. **The crash loop with no message.** `docker-compose.yml` interpolated `POSTGRES_USER`/`POSTGRES_DB` with no default and no `:?` guard, into `environment:`, which OVERRIDES `env_file`. Unset does not fall back — it resolves to the empty string, initdb creates a role and database named "", `DATABASE_URL` still says `eventsnap`, and the app hits `FATAL: role "eventsnap" does not exist` forever. `pg_isready -U "" -d ""` never passes, so `app` never turns healthy and Caddy — gated on `service_healthy` — never starts: port 443 dead for the whole event, exit only via `down -v`. Both now carry `:?` guards (verified they fire), and §3's ".env template — ALL of them" list, which omitted both, now includes them. Other runbook corrections: the backup/restore pointer named a line range that had drifted into an unrelated section and stopped mid-restore, before the media restore and the mandatory `chown` — now referenced by heading, which cannot go stale. §7.3 told you to verify that `EXPORT_PATH` is not pinned when §3 correctly says it is. Stale counts: rev-list 196 -> 217, "versions 007–022" -> 007–031, `frontend/Dockerfile:9` -> :8, and the low-disk description now matches the code (the 10 GB absolute floor was removed as unreachable). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
10
.env.example
10
.env.example
@@ -87,7 +87,15 @@ SESSION_EXPIRY_DAYS=30
|
||||
ADMIN_PASSWORD_HASH='$2y$12$placeholder_replace_me'
|
||||
|
||||
# ── Event ─────────────────────────────────────────────────────────────────────
|
||||
EVENT_NAME=Max & Maria's Wedding
|
||||
# DOUBLE-QUOTED, and it matters. Compose's env_file parser reads `Max & Maria's Wedding`
|
||||
# unquoted just fine — but the runbook also tells you to `set -a; . ./.env; set +a` in a plain
|
||||
# shell, and POSIX `sh` aborts on the apostrophe with "Unterminated quoted string" (rc=2).
|
||||
# Everything defined BELOW this line is then left unset, silently: the hourly pg_dump cron in
|
||||
# §10.2 does exactly this, so it would exit before ever writing a backup, every hour, into a log
|
||||
# nobody reads. Double quotes are read identically by both parsers (verified) — keep them, and
|
||||
# keep them double, since single quotes would make a literal `$` in a name survive but are what
|
||||
# `ADMIN_PASSWORD_HASH` above needs for the opposite reason.
|
||||
EVENT_NAME="Max & Maria's Wedding"
|
||||
EVENT_SLUG=max-maria-2026
|
||||
|
||||
# ── Storage ───────────────────────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user