v1.1.0 Phase 2: container hardening, backups, CD rollback #95

Merged
claude-bot merged 1 commit from feat/v1.1.0-phase2-infra into main 2026-07-18 16:54:05 +00:00
Contributor

Infra & deployment hardening — the second v1.1.0 slice.

#64 — Container hardening

  • App runs as a non-root user (UID 10001) via a gosu entrypoint that chowns the mounted cache volumes and drops privileges.
  • Entrypoint assembles DATABASE_URL from POSTGRES_* components with a URL-encoded password (so @ : / % in the password no longer breaks the DSN); an explicit DATABASE_URL still wins (CI/tests/dev unaffected).
  • Dockerfile + compose healthcheck on /health; port bind defaults to 127.0.0.1 (override APP_BIND=0.0.0.0); configurable mem_limit.

#67 — Postgres backups

  • scripts/backup.sh (pg_dump → gzip → prune, host or direct mode) + an opt-in db-backup compose profile writing to ./backups (outside the DB volume). README documents enabling it (or host cron) and the restore procedure. Complementary to any Proxmox/VM snapshots.

#66 — CD rollback + prod docs

  • cd.yml captures the deployed ref before pulling; if the post-deploy health check fails, it rolls back to that ref and rebuilds (failing the run) instead of leaving the dev box mid-deploy. Happy path unchanged.
  • README gains a manual production deploy + rollback section (the automated prod path remains a future enhancement — no prod infra assumed).

Verified on the dev server (real docker build + run)

  • Image builds; PID 1 = uvicorn, Uid 10001 (non-root); a special-character DB password round-trips through the encoded DSN (p%40s%3Aw%2Frd%25%23); migrations run and /health returns 200; non-root volume writes work.
  • No app/test code changed — the test suite is unaffected.

Note

The loopback-bind default is a deliberate hardening change. I've already set APP_BIND=0.0.0.0 in the dev server's .env so this branch's CD deploy keeps the dev app reachable on the LAN; production should sit behind a TLS reverse proxy.

Closes #64, #67, #66

🤖 Generated with Claude Code

Infra & deployment hardening — the second v1.1.0 slice. ## #64 — Container hardening - App runs as a **non-root user** (UID 10001) via a `gosu` entrypoint that chowns the mounted cache volumes and drops privileges. - Entrypoint assembles `DATABASE_URL` from `POSTGRES_*` components with a **URL-encoded password** (so `@ : / %` in the password no longer breaks the DSN); an explicit `DATABASE_URL` still wins (CI/tests/dev unaffected). - Dockerfile + compose **healthcheck** on `/health`; port bind defaults to **127.0.0.1** (override `APP_BIND=0.0.0.0`); configurable `mem_limit`. ## #67 — Postgres backups - `scripts/backup.sh` (pg_dump → gzip → prune, host or direct mode) + an **opt-in** `db-backup` compose profile writing to `./backups` (outside the DB volume). README documents enabling it (or host cron) and the restore procedure. Complementary to any Proxmox/VM snapshots. ## #66 — CD rollback + prod docs - `cd.yml` captures the deployed ref before pulling; if the post-deploy health check fails, it **rolls back** to that ref and rebuilds (failing the run) instead of leaving the dev box mid-deploy. Happy path unchanged. - README gains a manual **production deploy + rollback** section (the automated prod path remains a future enhancement — no prod infra assumed). ## Verified on the dev server (real docker build + run) - Image builds; **PID 1 = `uvicorn`, Uid 10001** (non-root); a special-character DB password round-trips through the encoded DSN (`p%40s%3Aw%2Frd%25%23`); migrations run and `/health` returns 200; non-root volume writes work. - No app/test code changed — the test suite is unaffected. ## Note The loopback-bind default is a deliberate hardening change. I've already set `APP_BIND=0.0.0.0` in the dev server's `.env` so this branch's CD deploy keeps the dev app reachable on the LAN; production should sit behind a TLS reverse proxy. Closes #64, #67, #66 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Harden container, add Postgres backups, add CD rollback + prod docs
All checks were successful
CI / test (pull_request) Successful in 3m33s
7d4fc24eaa
Container (#64): run the app as a non-root user (UID 10001) via a gosu entrypoint
that chowns the mounted cache volumes and assembles DATABASE_URL from POSTGRES_*
components with a URL-encoded password (an explicit DATABASE_URL still wins).
Add a Dockerfile + compose healthcheck on /health, default the port bind to
127.0.0.1 (override APP_BIND=0.0.0.0), and set a configurable memory limit.

Backups (#67): scripts/backup.sh (pg_dump -> gzip -> prune) plus an opt-in
`db-backup` compose profile writing to ./backups outside the DB volume; README
documents enabling it (or host cron) and the restore procedure.

CD (#66): capture the deployed ref before pulling and, if the post-deploy health
check fails, roll back to it and rebuild (failing the run) instead of leaving the
dev box mid-deploy. README gains a manual production-deploy + rollback section.

Verified on the dev server: image builds, PID 1 runs as UID 10001, a
special-character DB password round-trips through the encoded DSN, migrations run
and /health returns 200.

Closes #64, #67, #66

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
claude-bot deleted branch feat/v1.1.0-phase2-infra 2026-07-18 16:54:05 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
rbrooks/WeatherBot!95
No description provided.