# Supabase Notes ## Overview Supabase is a multi-component application. All components run in the `supabase` namespace: | Component | Image | Role | |-----------|-------|------| | `db` | supabase/postgres | PostgreSQL with Supabase extensions | | `kong` | kong/kong | API gateway, entry point for all traffic | | `auth` | supabase/gotrue | JWT-based authentication | | `rest` | postgrest/postgrest | Auto-generated REST API from Postgres | | `realtime` | supabase/realtime | WebSocket subscriptions | | `storage` + `imgproxy` | supabase/storage-api + darthsim/imgproxy | File storage (imgproxy as sidecar) | | `meta` | supabase/postgres-meta | DB introspection for Studio | | `studio` | supabase/studio | Web dashboard (protected by Kong basic-auth) | Traffic flows: browser → Traefik → Kong (port 8000) → individual services. ## First-Deploy Checklist 1. Add and deploy with: `wild app add supabase && wild app deploy supabase` 2. Wait for postgres to initialize (~2–3 min on first boot — it runs SQL init scripts) 3. Access the Studio at `https://supabase.` using the dashboard credentials 4. **Replace example API keys before going to production** (see below) ## JWT Keys — MUST Replace for Production The default `jwtSecret`, `anonKey`, and `serviceRoleKey` in `secrets.yaml` are Supabase's well-known **example keys** (publicly known, expire Jan 2027). They are sufficient for testing but **must not be used in production**. Generate new keys using the built-in script: ```bash wild app run supabase generate-keys ``` Then copy the output values into `secrets.yaml` and redeploy: ```bash wild app deploy supabase ``` The postgres database stores the JWT secret in its settings (`app.settings.jwt_secret`). After replacing keys, the postgres pod must restart to pick up the new secret — a redeploy handles this. ## Postgres — Internal Database Supabase uses its own dedicated postgres instance (`supabase/postgres` image) rather than the shared Wild Cloud postgres app. This image includes: - ~390 extensions pre-compiled (pgjwt, wal2json, vector, pg_net, etc.) - Supabase service roles (supabase_admin, authenticator, supabase_auth_admin, supabase_storage_admin, etc.) - Initialization scripts for realtime, webhooks, JWT settings All Supabase service roles (auth, storage, realtime, etc.) use the same password as the postgres admin (`dbPassword` secret). This is the Supabase default behavior. ### Bootstrap User: supabase_admin `POSTGRES_USER` is set to `supabase_admin` (not `postgres`). This is required because the image's `migrate.sh` script connects immediately as `supabase_admin` before running any init scripts. Using `POSTGRES_USER=postgres` would make postgres the bootstrap superuser, which prevents `migrate.sh` from demoting it (PostgreSQL prohibits removing SUPERUSER from the bootstrap user). With `POSTGRES_USER=supabase_admin`: 1. `initdb` creates `supabase_admin` as the bootstrap superuser 2. `migrate.sh` connects as `supabase_admin` and creates the `postgres` role 3. After init-scripts run, `migrate.sh` demotes `postgres` to non-superuser (this is Supabase's intended model) The `postgres` user remains available with database-level privileges. All Supabase service roles use specific functional accounts (supabase_auth_admin, authenticator, etc.), not the postgres superuser. ### Volume Mount: /var/lib/postgresql (parent dir) The PVC is mounted at `/var/lib/postgresql` (the parent), NOT at `/var/lib/postgresql/data` (PGDATA). This is required because Longhorn's ext4 filesystem creates a `lost+found` directory, which blocks postgres `initdb` when the PVC is mounted directly at PGDATA. With the parent-dir mount, `initdb` creates `/var/lib/postgresql/data` as a subdirectory, leaving `lost+found` at the parent level where it doesn't interfere. ## Realtime Tenant ID The Realtime service must identify itself with the tenant ID `realtime-dev`. The Deployment sets `spec.template.spec.hostname: realtime-dev` so the Elixir application seeds itself with this tenant ID. The Kubernetes service for Realtime is also named `realtime-dev` so Kong can route to it correctly. ## Storage File storage uses the local filesystem backend by default. Objects are stored in the `supabase-storage` PVC. The `imgproxy` container is a sidecar in the same pod, sharing the storage PVC for image transformation. For S3-backed storage: edit `deployment-storage.yaml` to change `STORAGE_BACKEND` to `s3` and add S3 credentials as secrets. ## SMTP SMTP is required for auth email flows (email confirmation, password reset). Configure the `smtp` app dependency and it will be wired automatically. If you want to disable email confirmation during initial testing, you can temporarily set `GOTRUE_MAILER_AUTOCONFIRM=true` in `deployment-auth.yaml`. ## Studio Dashboard Credentials The Studio dashboard is protected by HTTP Basic Auth via Kong. Credentials: - Username: configured via `dashboardUsername` config key (default: `supabase`) - Password: auto-generated `dashboardPassword` secret To view the generated password: ```bash kubectl -n supabase get secret supabase-secrets -o jsonpath='{.data.dashboardPassword}' | base64 -d ``` ## Connecting Applications Applications connect to Supabase via the Kong API gateway: - **REST API**: `https://supabase./rest/v1/` - **Auth**: `https://supabase./auth/v1/` - **Realtime**: `wss://supabase./realtime/v1/` - **Storage**: `https://supabase./storage/v1/` Use the `anonKey` for client-side (browser) access and `serviceRoleKey` for server-side (admin) access. ## Supabase JavaScript Client ```js import { createClient } from '@supabase/supabase-js' const supabase = createClient( 'https://supabase.', '' // from secrets.yaml ) ``` ## Troubleshooting **Postgres won't start**: Check that the supabase/postgres image can be pulled and that the PVC is bound. **Auth returns 500**: Postgres is not ready or the init scripts haven't completed. Check DB pod logs and wait for all init SQL to finish. **Kong returns 401 for all requests**: The `anonKey` in secrets doesn't match the `jwtSecret`. Regenerate keys with `wild app run supabase generate-keys`. **Realtime health check fails**: The pod hostname may not be `realtime-dev`. Check the Deployment `spec.template.spec.hostname` field. **Studio shows "Project not found"**: Studio needs Kong to be healthy. Check Kong pod logs for declarative config errors.