The stack no longer talks to any specific external proxy. Removed traefik.*
labels and the shared `traefik` external network from kong + app, and dropped
TRAEFIK_NETWORK / TRAEFIK_ENTRYPOINT / TRAEFIK_CERTRESOLVER from the .env
template and the deploy-script bootstrap.
Internal edge is a `caddy:2-alpine` container on the `colectivo` network,
publishing host port 3000. `infra/Caddyfile.erosi` does the path split:
/rest|/auth|/realtime|/storage|/graphql|/pg -> kong:8000, everything else
-> app:3000. Global block sets `auto_https off` + `admin off` so :3000 is
the only listener.
Whatever proxy fronts the host (NetBird's Traefik, nginx, anything) just
terminates TLS for erosi.limonia.net and forwards plain HTTP to
ambrosio:3000. Off-stack and out of this repo.
Docs (CLAUDE.md + docs/deployment.md) updated to describe the two-layer
edge. Includes the gotcha that bind-mounted file edits (Caddyfile) need
`docker compose restart caddy` since `up -d` won't recreate the container
on file-only changes.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Ambrosio's TLS is now handled by the Traefik that NetBird's self-hosted
installer deploys. kong + app attach to that Traefik network and expose
themselves via Docker-provider labels (erosi-kong priority 100 for
Supabase API paths, erosi-app priority 1 for everything else on
erosi.limonia.net). No container publishes host ports; host Caddy is
out of the deploy path permanently.
Keycloak is no longer bundled — PUBLIC_KEYCLOAK_URL points at an
external IdP. The deploy script writes .env with FILL_IN_* placeholders
for PUBLIC_KEYCLOAK_URL, KEYCLOAK_CLIENT_SECRET, and TRAEFIK_NETWORK,
and fails fast until they are filled.
New domain: erosi.limonia.net. realm-export.erosi.json is retained
as reference for the external Keycloak operator; it is no longer
imported by this stack.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Self-contained compose for a single-VPS deploy behind the host's Caddy.
App + Supabase API share erosi.oier.ovh; Keycloak on auth.oier.ovh.
- infra/docker-compose.erosi.yml — full stack bound to 127.0.0.1 only
(db, auth, rest, realtime, storage, imgproxy, kong, meta, keycloak, app)
- infra/caddy/erosi.Caddyfile — host-Caddy snippet with TLS + path routing
(/auth /rest /storage /realtime /graphql → kong, rest → app)
- infra/scripts/deploy-erosi.sh — rsync + first-run secret generation +
build + migrations + Caddy snippet install
- keycloak/realm-export.erosi.json — prod redirect URIs, registration on,
client secret as __KEYCLOAK_CLIENT_SECRET__ placeholder (deploy sub'd)
- .env.erosi.example — env template with placeholders
Supporting fixes to make the deploy work on a clean Supabase-postgres volume:
- infra/db-init/00-role-passwords.sh — rewrote as idempotent bootstrap:
CREATE the Supabase service roles if missing, set passwords on pre-existing
ones, enable pg_cron/pgcrypto/uuid-ossp, create auth/storage/graphql_public/
_realtime/realtime schemas, create empty supabase_realtime publication,
set per-role search_path (auth→auth, storage→storage). Old version only
ALTERed passwords and relied on the roles already existing — worked for a
grandfathered dev volume, failed on a fresh prod init.
- infra/db-init/00-role-passwords.sql — removed (folded into .sh).
- apps/web/vite.config.ts — PWA strategy generateSW (was injectManifest).
The plugin's injectManifest hardcodes the SW source filename to
service-worker.js and ignores the filename: 'sw.ts' override, making the
production build fail. generateSW's auto-generated precache-only SW is
functionally equivalent to our 5-line src/sw.ts.
Tested end-to-end on ambrosio: all 9 containers up, 11 migrations applied,
https://erosi.oier.ovh returns 200, Keycloak OIDC discovery serves the
correct issuer, /auth/v1/settings lists Keycloak as the sole external
provider.