Skip to main content

Certification track: Professional Cloud Database Engineer (PCDE)

Supabase Common — Shared Application Configuration

Supabase_Common is the shared application layer for Supabase. It is not deployed on its own; instead it supplies the Supabase-specific configuration that Supabase_GKE builds on, ensuring that the gateway, database schema, and secrets are consistently wired together. End users never configure this layer directly — it has no deployment UI inputs of its own — but understanding what it provides explains the defaults you see in the platform docs.

For the infrastructure that actually provisions and runs Supabase, see the platform guide (Supabase_GKE) and the foundation guides (App_GKE, App_Common).


1. What this layer provides

AreaProvided by Supabase_CommonWhere it surfaces
JWT credentialsGenerates and stores the JWT signing secret, anon key, service role key, publishable key, secret key, and secret_key_base in Secret ManagerRetrieve via Secret Manager; placeholders require post-deploy replacement
Container imagePins the Kong API gateway image and the Cloud Build configuration that extends itcontainer_image output of the platform deployment
Database engineFixes PostgreSQL 15 as the only supported engine (Supabase_GKE runs it in-namespace, not on Cloud SQL)§Database in the platform guide
Database bootstrapDefines the first-deploy db-init job that sets the Supabase service-role passwords and creates the Supabase schemas/grantsinitialization_jobs output
Object storageDeclares the Cloud Storage storage bucket (name_suffix = "storage")storage_buckets output
Kong configurationSets the baseline Kong environment (DB-less mode, declarative config path, routing ports, proxy buffer settings)Application behaviour in the platform guide
Health checksDeclares startup_probe/liveness_probe variable defaults (HTTP /health) — not what is actually deployed; Supabase_GKE overrides both to TCP§5 below and §Observability in the platform guide

2. JWT credentials in Secret Manager

Supabase authentication relies on JWTs signed by a shared secret. Six secrets are stored in Secret Manager by this layer:

Secret suffixContentAuto-generated?
-jwt-secret32-char JWT signing secretYes — random if jwt_secret is empty
-anon-keyPublic anonymous JWTNo — placeholder; must be replaced
-service-role-keyService role JWTNo — placeholder; must be replaced
-publishable-keyPublishable (anon) opaque API keyNo — placeholder if empty
-secret-keyServer-side opaque API keyNo — placeholder if empty
-key-base64-char secret_key_base for Realtime/SupavisorYes — random if secret_key_base is empty

The anon key and service role key are placeholders on first deploy. Replace them with valid JWTs signed by the jwt_secret:

# 1. Retrieve the auto-generated JWT signing secret:
gcloud secrets versions access latest --secret="<prefix>-jwt-secret" --project "$PROJECT"

# 2. Generate JWTs at https://jwt.io or https://supabase.com/docs/guides/self-hosting/docker#generate-api-keys
# Anon payload: { "role": "anon", "iss": "supabase" }
# Service role payload: { "role": "service_role", "iss": "supabase" }

# 3. Upload the anon JWT:
echo -n "<anon-jwt>" | gcloud secrets versions add "<prefix>-anon-key" \
--data-file=- --project "$PROJECT"

# 4. Upload the service role JWT:
echo -n "<service-role-jwt>" | gcloud secrets versions add "<prefix>-service-role-key" \
--data-file=- --project "$PROJECT"

# 5. Restart the Kong pod to pick up the updated secrets:
kubectl rollout restart deploy/<kong-workload> -n "<namespace>"

The database password is generated and managed separately by the foundation; its secret name is reported in the platform deployment outputs (database_password_secret). See App_Common for the shared secret and Workload Identity model.


3. Database engine and bootstrap

Supabase requires PostgreSQL 15; the engine is fixed and no other database is supported. The Common config declares database_type = "POSTGRES_15", but Supabase_GKE overrides it to "NONE" on the foundation and runs the supabase/postgres image as an in-namespace service instead — so there is no Cloud SQL instance and no Auth Proxy in a deployed Supabase.

On the first deployment a one-shot db-init job connects to that in-namespace Postgres as supabase_admin and idempotently:

  1. sets LOGIN passwords on the service roles the supabase/postgres image creates but leaves password-less (authenticator, supabase_auth_admin, supabase_storage_admin),
  2. creates the auth, storage, _realtime, and realtime schemas and the public-schema grants/default privileges for anon, authenticated, and service_role,
  3. sets the database-level app.settings.jwt_secret / jwt_exp GUCs used by RLS.

The job uses the mirror.gcr.io/library/postgres:15-alpine image (for psql) and runs scripts/db-init.sh. It is safe to re-run. Inspect the database directly:

kubectl exec -n "<namespace>" deploy/<postgres-workload> -- psql -U supabase_admin -d postgres

The namespace and service names are in the platform deployment outputs.


4. Kong API gateway configuration

Supabase_Common configures Kong to run in declarative (DB-less) mode. All routing is defined in a kong.yml file baked into the Kong container image by the Dockerfile in scripts/. No Kong database is provisioned or required.

Key Kong environment variables set by this layer:

VariableValuePurpose
KONG_DATABASEoffDB-less declarative mode
KONG_DECLARATIVE_CONFIG/home/kong/kong.ymlPath to routing config
KONG_PLUGINSrequest-transformer,cors,key-auth,aclActive plugins
KONG_PROXY_LISTEN0.0.0.0:8000Public HTTP proxy port
KONG_ADMIN_LISTEN0.0.0.0:8001Admin API port
SUPABASE_PORT8000Kong listen port (referenced by Supabase services)

Kong routes requests to microservices by path prefix as defined in kong.yml:

Path prefixTarget servicePort
/auth/v1/*GoTrue (authentication)9999
/rest/v1/*PostgREST (REST API)3000
/realtime/v1/*Realtime (WebSocket)4000
/storage/v1/*Storage API5000

URL configuration variables (site_url, api_external_url, supabase_public_url, jwt_expiry, pgrst_db_schemas) are injected into the Kong container environment so that GoTrue and PostgREST receive the correct external addresses. Update these to real public URLs before production use — the localhost defaults prevent OAuth flows from functioning outside the cluster.


5. Health probe behaviour

The startup_probe/liveness_probe variables declared here default to HTTP /health:

ProbeTypePathInitial delayPeriodFailure threshold
StartupHTTP/health30 s10 s18
LivenessHTTP/health60 s30 s3

These defaults are never actually deployed. kong.yml is a DB-less declarative config that only defines routes for /rest/v1, /auth/v1, /realtime/v1, /storage/v1, /pg, and / (Studio) — there is no /health route, so an HTTP probe against it would 404. Supabase_GKE/main.tf unconditionally overrides both probes to TCP against the Kong container port in its supabase_module merge, regardless of the value passed in for these variables. Treat this section as documenting the variable's shape only, not the probe behaviour you will observe on a deployed instance — see docs/modules/Supabase_GKE.md for what is actually applied.


6. Object storage

A Cloud Storage bucket with the suffix -storage is declared here and provisioned by the foundation, which also grants the workload service account access. Supabase file uploads flow through the Storage API microservice to this bucket. Public-access prevention is set to inherited so individual objects can be served publicly via bucket-level ACLs when needed. List it with:

gcloud storage buckets list --project "$PROJECT"

For the Supabase-specific, user-facing configuration (variables by group, outputs, and how to explore each service from the Console and CLI), see the platform guide: Supabase_GKE. For the infrastructure layer that runs the workload, see App_GKE and App_Common.