Files
collective-lists/docs/deployment.md
Oier Bravo Urtasun 768b3cda58 docs: split CLAUDE.md into docs/{development,deployment,history/fase-*}
CLAUDE.md shrinks to rules + status + index (175 lines, down from 476).
Dev setup, prod deploy, and per-phase build records move to docs/.
Gotchas regrouped by domain (auth, frontend, PWA, testing, backend, deploy,
platform) and unnumbered so they don't drift as new ones land.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 22:21:20 +02:00

3.1 KiB

Deployment

Production stack — ambrosio (OVH VPS)

Live URLs: https://erosi.oier.ovh (app + Supabase API, single-domain) + https://auth.oier.ovh (Keycloak). OVH VPS, Ubuntu 24, Docker 29.

  • infra/docker-compose.erosi.yml — full stack bound to 127.0.0.1 only (db, auth, rest, realtime, storage, imgproxy, kong, keycloak, app). studio + meta intentionally dropped for prod.
  • infra/caddy/erosi.Caddyfile — snippet appended to the host's /etc/caddy/Caddyfile between # BEGIN colectivo-erosi / # END colectivo-erosi markers. Host Caddy 2.11.2 handles TLS (Let's Encrypt via tls-alpn-01). erosi.oier.ovh routes /auth/v1/*, /rest/v1/*, /realtime/v1/*, /storage/v1/*, /graphql/v1/*, /pg/* → Kong :8000; everything else → SvelteKit :3000. auth.oier.ovh → Keycloak :8090 with X-Forwarded-Port explicitly (Caddy auto-sets Proto/Host/For).
  • infra/scripts/deploy-erosi.sh — idempotent redeploy: rsync → generate secrets on first run (via infra/scripts/rotate-jwt.sh) → substitute __KEYCLOAK_CLIENT_SECRET__ in keycloak/realm-export.erosi.json → docker compose build + up → run pending migrations → re-install Caddy snippet → reload.
  • keycloak/realm-export.erosi.json — prod realm: registrationAllowed: true, redirect URIs scoped to https://erosi.oier.ovh/*, client secret placeholder that the deploy script substitutes with a per-install random value. No dev users.
  • .env.erosi.example — committed template. The real /opt/colectivo/.env on ambrosio is 600 and stays on the server.
  • UFW allows 80/tcp, 443/tcp, 22/tcp. All other ports remain DENY. Docker services listen on 127.0.0.1 only, so Kong / Keycloak / the app are unreachable from the public internet except via Caddy.

Prod-specific fixes

Had to be made for the stack to boot cleanly on a fresh volume:

  • infra/db-init/00-role-passwords.sh — rewrote as idempotent bootstrap (see the "supabase/postgres doesn't bootstrap Supabase roles" gotcha in CLAUDE.md): creates all Supabase service roles if absent, enables pg_cron + pgcrypto + uuid-ossp, creates auth / storage / graphql_public / _realtime / realtime schemas + empty supabase_realtime publication + keycloak database + role, sets supabase_auth_admin search_path to auth. The old 00-role-passwords.sql (ALTER-only) has been removed.
  • apps/web/vite.config.ts — PWA strategy switched injectManifestgenerateSW (see the "injectManifest hardcoded filename" gotcha in CLAUDE.md).
  • Keycloak container uses start --import-realm (no --optimized) so Quarkus re-builds with the Postgres driver on first boot; with --optimized it silently falls back to H2 and fails with "jdbc:h2 URL format error".

Keycloak admin password

Generated per-install — retrieve with ssh ambrosio 'grep KEYCLOAK_ADMIN_PASSWORD /opt/colectivo/.env'. Console at https://auth.oier.ovh/admin.

Not yet configured on ambrosio

  • SMTP (Resend)
  • Automated backups — script exists at infra/scripts/backup.sh, not scheduled
  • Lighthouse run against prod URL
  • Final production icons