Files
wild-directory/supabase/notes.md
Paul Payne 4d983819c9 feat(supabase): add services and statefulset for database management
feat(synapse): update ingress to use traefik ingress class and bump version

feat(syncthing-discovery): introduce syncthing discovery service with deployment and ingress

feat(syncthing-relay): add syncthing relay server with deployment and ingress configuration

fix(taiga): update liveness and readiness probes to use tcpSocket for health checks

fix(taiga): change PVC access mode to ReadWriteMany for media and static storage

feat(traefik): add icon and ignore rules for traefik service

docs(ushahidi): add notes for Redis configuration and Laravel startup probe adjustments

feat(ushahidi): implement dedicated Redis deployment for Ushahidi

fix(vllm): update deployment strategy and readiness/liveness probes for improved stability

fix(writefreely): pin writefreely image version to v0.15.1 for consistency

docs(zulip): add notes for TLS-terminating reverse proxy configuration and expected behavior
2026-07-02 21:34:27 +00:00

6.3 KiB
Raw Permalink Blame History

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 (~23 min on first boot — it runs SQL init scripts)
  3. Access the Studio at https://supabase.<your-domain> 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:

wild app run supabase generate-keys

Then copy the output values into secrets.yaml and redeploy:

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:

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.<domain>/rest/v1/
  • Auth: https://supabase.<domain>/auth/v1/
  • Realtime: wss://supabase.<domain>/realtime/v1/
  • Storage: https://supabase.<domain>/storage/v1/

Use the anonKey for client-side (browser) access and serviceRoleKey for server-side (admin) access.

Supabase JavaScript Client

import { createClient } from '@supabase/supabase-js'

const supabase = createClient(
  'https://supabase.<your-domain>',
  '<anonKey>'  // 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.