Modules / AI and operations
Supabase self-hosted
Run the full Supabase stack (Postgres, Supavisor, Storage) on your own host.
Setup and configuration
Follow the official self-hosting guide: clone supabase/supabase, copy docker/.env.example to docker/.env, and change every secret — POSTGRES_PASSWORD, JWT_SECRET, ANON_KEY, SERVICE_ROLE_KEY (regenerate with your JWT secret), DASHBOARD_USERNAME/DASHBOARD_PASSWORD, SECRET_KEY_BASE, VAULT_ENC_KEY, and the PG_META_CRYPTO_KEY. Start with docker compose -f docker/docker-compose.yml up -d.
This kit uses Better Auth, not Supabase Auth — you do not need to configure GoTrue providers for the kit; keep the bundled auth service internal if nothing else uses it. Supabase here supplies Postgres (via Supavisor) and S3-compatible Storage only.
Supavisor exposes the session pooler on 5432 and transaction pooler on 6543. Usernames must be tenant-qualified on the pooler: postgres.<tenant-id> — the tenant id is your project ref configured in the pooler env (default pooler_tenant_id). Direct Postgres (port 5432 inside the network or the mapped port) works with plain postgres.
Expose only what you need: the Kong gateway (8000/8443) fronts Studio, PostgREST, Storage, and Auth; database ports should stay on a private network or behind a firewall — never publish 5432/6543 publicly. Put TLS in front of Kong via your own reverse proxy; the Postgres port speaks TLS only if you configure certificates in the container.
Studio is protected by DASHBOARD_USERNAME/DASHBOARD_PASSWORD at the Kong layer — change both before exposing 8000. Keep 4000 (Kong admin) firewalled.
DATABASE_URL=postgres://postgres.<tenant>:<password>@<host>:5432/postgres
# serverless functions: port 6543 adds ?pgbouncer=true (prepare is disabled automatically)
STORAGE_PROVIDER=s3
STORAGE_ENDPOINT=https://<host>:8000/storage/v1/s3
STORAGE_REGION=us-east-1 # required by the S3 protocol; self-hosted accepts any region value
STORAGE_FORCE_PATH_STYLE=trueImplementation workflow
Generate S3 credentials for self-hosted Storage in Studio → Storage → S3 connection settings (or seed them via the storage env in docker-compose); then set STORAGE_* in .env like the cloud guide. Run migrations with pnpm --filter @supastack/db db:migrate against the session pooler or direct Postgres — never the transaction pooler.
Run pnpm supabase:doctor -- --fix-rls after migrating: self-hosted PostgREST exposes public-schema tables to the anon/authenticated roles exactly like cloud, so the kit's tables must have RLS enabled and the API roles revoked. The doctor prints the exact ALTER/REVOKE statements and applies them in one transaction.
Upgrades: pull new images, pg_dump your database volume first, and re-run migrations. For backups, cron pg_dump against the direct Postgres port — the transaction pooler is unsuitable for dumps.
Verification and troubleshooting
Failure modes: 'prepared statement ... does not exist' → you are on :6543 without pgbouncer handling (the kit disables prepare there automatically — verify the URL). 'too many connections' → Supavisor pool exhausted; lower maxConnections or raise pool_size in the pooler env. 'Tenant or user not found' → the pooler username must be postgres.<tenant-id>. If Studio loads but API calls fail, check Kong is proxying /rest/v1 and /storage/v1 and that JWT keys match the regenerated JWT_SECRET.