A Kubernetes operator for deploying and managing self-hosted Supabase instances.
The Supabase Operator enables you to deploy complete Supabase instances on Kubernetes using a single Custom Resource Definition (CRD). It manages all Supabase components including Kong API Gateway, GoTrue authentication, PostgREST, Realtime, Storage API, and Meta.
Features:
- 🚀 Deploy full Supabase stack with a single manifest
- 🔐 Automatic JWT secret generation
- 📊 Granular status reporting with per-component tracking
- 🔄 Rolling updates with health checks
- 🏗️ Multi-tenant: Multiple Supabase projects per namespace
- ☸️ Kubernetes-native with standard types and patterns
The operator manages:
- Kong: API Gateway (v2.8.1)
- Auth: GoTrue authentication service (v2.177.0)
- PostgREST: Automatic REST API (v12.2.12)
- Realtime: WebSocket server (v2.34.47)
- Storage API: File storage service (v1.25.7)
- Meta: PostgreSQL metadata service (v0.91.0)
- Studio: Supabase management UI (2025.10.01-sha-8460121)
External dependencies (user-provided):
- PostgreSQL database
- S3-compatible storage
- Kubernetes 1.33+
- kubectl configured
- External PostgreSQL database
- S3-compatible storage (MinIO, AWS S3, etc.)
kubectl apply -f https://raw.githubusercontent.com/strrl/supabase-operator/main/config/install.yamlOr install from source:
git clone https://github.com/strrl/supabase-operator
cd supabase-operator
make install
make deploykubectl create secret generic postgres-config \
--from-literal=host=postgres.example.com \
--from-literal=port=5432 \
--from-literal=database=supabase \
--from-literal=username=postgres \
--from-literal=password=your-secure-passwordkubectl create secret generic s3-config \
--from-literal=endpoint=https://s3.example.com \
--from-literal=region=us-east-1 \
--from-literal=bucket=supabase-storage \
--from-literal=accessKeyId=your-access-key \
--from-literal=secretAccessKey=your-secret-keyNote: Use camelCase for secret keys:
accessKeyIdandsecretAccessKey(not kebab-case)
kubectl create secret generic studio-dashboard-creds \
--from-literal=username=supabase \
--from-literal=password='choose-a-strong-password'Change these credentials before exposing Kong publicly.
apiVersion: supabase.strrl.dev/v1alpha1
kind: SupabaseProject
metadata:
name: my-supabase
namespace: default
spec:
projectId: my-supabase-project
database:
secretRef:
name: postgres-config
sslMode: require
maxConnections: 50
storage:
secretRef:
name: s3-config
forcePathStyle: true
studio:
dashboardBasicAuthSecretRef:
name: studio-dashboard-credsApply the manifest:
kubectl apply -f my-supabase.yamlkubectl get supabaseproject my-supabase -o yamlCheck component status:
kubectl get supabaseproject my-supabase -o jsonpath='{.status.components}'The operator creates services for each component:
kubectl get services -l app.kubernetes.io/part-of=supabaseAccess Kong API Gateway:
kubectl port-forward svc/my-supabase-kong 8000:8000Requests to http://localhost:8000/ will answer 401 Unauthorized until you supply the username/password stored in studio-dashboard-creds.
The operator generates API keys and stores them in a secret named <project>-jwt within the same namespace as your SupabaseProject.
# Get the public ANON key
ANON_KEY=$(kubectl get secret my-supabase-jwt \
-o jsonpath='{.data.anon-key}' | base64 -d)
# Get the Service Role key
SERVICE_ROLE_KEY=$(kubectl get secret my-supabase-jwt \
-o jsonpath='{.data.service-role-key}' | base64 -d)
# Optional: discover the API endpoint
API_URL=$(kubectl get supabaseproject my-supabase \
-o jsonpath='{.status.endpoints.api}')Use $ANON_KEY for client-side requests and $SERVICE_ROLE_KEY for trusted backend workflows.
Supabase components use the external PostgreSQL database you referenced via postgres-config. You can reuse the same credentials to connect with tools like psql.
POSTGRES_HOST=$(kubectl get secret postgres-config -o jsonpath='{.data.host}' | base64 -d)
POSTGRES_PORT=$(kubectl get secret postgres-config -o jsonpath='{.data.port}' | base64 -d)
POSTGRES_DB=$(kubectl get secret postgres-config -o jsonpath='{.data.database}' | base64 -d)
POSTGRES_USER=$(kubectl get secret postgres-config -o jsonpath='{.data.username}' | base64 -d)
POSTGRES_PASSWORD=$(kubectl get secret postgres-config -o jsonpath='{.data.password}' | base64 -d)
psql "postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}"If the database is only reachable inside the cluster (for example, over a private network), run kubectl run with a temporary pod or establish a VPN/tunnel that matches your deployment topology.
Override default images and resources:
spec:
kong:
image: kong:3.0.0
replicas: 2
resources:
limits:
memory: "4Gi"
cpu: "1000m"
requests:
memory: "2Gi"
cpu: "500m"
extraEnv:
- name: KONG_LOG_LEVEL
value: "debug"| Component | Memory Limit | CPU Limit | Memory Request | CPU Request |
|---|---|---|---|---|
| Kong | 2.5Gi | 500m | 1Gi | 250m |
| Auth | 128Mi | 100m | 64Mi | 50m |
| PostgREST | 256Mi | 200m | 128Mi | 100m |
| Realtime | 256Mi | 200m | 128Mi | 100m |
| Storage | 128Mi | 100m | 64Mi | 50m |
| Meta | 128Mi | 100m | 64Mi | 50m |
On first deployment, the operator automatically initializes your PostgreSQL database with:
Extensions:
pgcrypto- Cryptographic functionsuuid-ossp- UUID generationpg_stat_statements- Query statistics
Schemas:
auth- Authentication datastorage- File metadatarealtime- Real-time subscriptions
Roles:
authenticator- API request authenticator roleanon- Anonymous access roleservice_role- Service-level access role with RLS bypass
All initialization operations are idempotent and safe to re-run.
The operator provides granular status reporting:
status:
phase: Running
message: "All components running"
conditions:
- type: Ready
status: "True"
reason: AllComponentsReady
- type: Progressing
status: "False"
reason: ReconciliationComplete
components:
kong:
phase: Running
ready: true
version: kong:2.8.1
replicas: 1
readyReplicas: 1
auth:
phase: Running
ready: true
version: supabase/gotrue:v2.177.0Phases:
Pending: Initial stateValidatingDependencies: Checking PostgreSQL and S3DeployingSecrets: Generating JWT secretsDeployingComponents: Creating deploymentsRunning: All components healthyFailed: Reconciliation error
The operator exposes Prometheus metrics at :8443/metrics:
kubectl port-forward -n supabase-operator-system \
svc/supabase-operator-controller-manager-metrics-service 8443:8443Key metrics:
controller_runtime_reconcile_total- Total reconciliationscontroller_runtime_reconcile_errors_total- Reconciliation errorscontroller_runtime_reconcile_time_seconds- Reconciliation durationworkqueue_depth- Controller work queue depthworkqueue_adds_total- Items added to work queue
If using Prometheus Operator, the operator includes a ServiceMonitor:
kubectl apply -k config/prometheusCheck secrets exist:
kubectl get secret postgres-config s3-configVerify secret keys:
kubectl get secret postgres-config -o jsonpath='{.data}' | jqRequired database secret keys: host, port, database, username, password
Required storage secret keys: endpoint, region, bucket, accessKeyId, secretAccessKey
Check status message:
kubectl get supabaseproject my-supabase -o jsonpath='{.status.message}'Check conditions:
kubectl get supabaseproject my-supabase -o jsonpath='{.status.conditions}' | jqView controller logs:
kubectl logs -n supabase-operator-system \
-l control-plane=controller-manager \
--tail=100Check database connectivity:
kubectl run -it --rm debug --image=postgres:16 --restart=Never -- \
psql -h postgres.example.com -U postgres -d supabaseVerify database permissions:
The database user must have CREATEDB or superuser privileges to create extensions and schemas.
Check pod status:
kubectl get pods -l app.kubernetes.io/part-of=supabaseCheck pod logs:
kubectl logs my-supabase-kong-xxxVerify resource limits: Ensure your cluster has sufficient resources for all components.
- Go 1.22+
- Kubebuilder 4.0+
- Docker
make buildmake testmake install # Install CRDs
make run # Run controller locallymake manifests # Generate CRD and RBAC
make generate # Generate deepcopy codeSee future-considerations.md for deferred features and architectural flexibility.
Contributions welcome! Please read the design documents for context.
MIT
- v1alpha1: Core operator with basic deployment
- v1beta1: Advanced features (HA, backup, monitoring)
- v1: Production-ready with stability guarantees
See future-considerations.md for planned features.
The e2e suite spins up a temporary Minikube profile, deploys the operator, and exercises a SupabaseProject end-to-end (including capturing a Kong Studio screenshot via headless Chrome).
-
Install Minikube (
minikube versionshould succeed) and ensure Docker is available. -
Install Google Chrome or Chromium locally, or set
E2E_CHROME_PATHto a compatible executable. If Chrome is missing, the screenshot spec is skipped. -
Run:
MINIKUBE_START_ARGS="--driver=docker --cpus=4 --memory=8192 --wait=all" make test-e2eThis command creates the
supabase-operator-test-e2eMinikube profile, runsgo test -tags=e2e, saves screenshots to.artifacts/screenshots/, and tears the profile down afterwards.