services: supabase-kong: image: 'kong:3.9.1' user: root entrypoint: 'bash -c ''eval "echo \"$$(cat /home/kong/temp.yml)\"" > /home/kong/kong.yml && /docker-entrypoint.sh kong docker-start''' environment: - SERVICE_URL_SUPABASEKONG_8000 - 'KONG_PORT_MAPS=443:8000' - 'JWT_SECRET=${SERVICE_PASSWORD_JWT}' - KONG_DATABASE=off - KONG_DECLARATIVE_CONFIG=/home/kong/kong.yml - 'KONG_DNS_ORDER=LAST,A,CNAME' - 'KONG_PLUGINS=request-transformer,cors,key-auth,acl,basic-auth' - KONG_NGINX_PROXY_PROXY_BUFFER_SIZE=160k - 'KONG_NGINX_PROXY_PROXY_BUFFERS=64 160k' - 'SUPABASE_ANON_KEY=${SERVICE_SUPABASEANON_KEY}' - 'SUPABASE_SERVICE_KEY=${SERVICE_SUPABASESERVICE_KEY}' - 'DASHBOARD_USERNAME=${SERVICE_USER_ADMIN}' - 'DASHBOARD_PASSWORD=${SERVICE_PASSWORD_ADMIN}' - KONG_PREFIX=/home/kong/ - TMPDIR=/tmp - KONG_PROXY_ACCESS_LOG=/dev/stdout - KONG_ADMIN_ACCESS_LOG=/dev/stdout - KONG_PROXY_ERROR_LOG=/dev/stderr - KONG_ADMIN_ERROR_LOG=/dev/stderr volumes: - './volumes/api/kong.yml:/home/kong/temp.yml:z' ports: - '8100:8000' supabase-studio: image: 'supabase/studio:2025.11.10-sha-5291fe3' healthcheck: test: - CMD - node - '-e' - "fetch('http://127.0.0.1:3000/api/platform/profile').then((r) => {if (r.status !== 200) throw new Error(r.status)})" timeout: 5s interval: 5s retries: 3 depends_on: supabase-analytics: condition: service_healthy environment: - HOSTNAME=0.0.0.0 - 'STUDIO_PG_META_URL=http://supabase-meta:8080' - 'POSTGRES_PASSWORD=${SERVICE_PASSWORD_POSTGRES}' - 'DEFAULT_ORGANIZATION_NAME=${STUDIO_DEFAULT_ORGANIZATION:-Default Organization}' - 'DEFAULT_PROJECT_NAME=${STUDIO_DEFAULT_PROJECT:-Default Project}' - 'SUPABASE_URL=http://localhost:8000' - 'SUPABASE_PUBLIC_URL=${SERVICE_URL_SUPABASEKONG}' - 'SUPABASE_ANON_KEY=${SERVICE_SUPABASEANON_KEY}' - 'SUPABASE_SERVICE_KEY=${SERVICE_SUPABASESERVICE_KEY}' - 'AUTH_JWT_SECRET=${SERVICE_PASSWORD_JWT}' - 'LOGFLARE_API_KEY=${SERVICE_PASSWORD_LOGFLARE}' - 'LOGFLARE_URL=http://supabase-analytics:4000' - 'SUPABASE_PUBLIC_API=${SERVICE_URL_SUPABASEKONG}' - NEXT_PUBLIC_ENABLE_LOGS=true - NEXT_ANALYTICS_BACKEND_PROVIDER=postgres - 'OPENAI_API_KEY=${OPENAI_API_KEY}' supabase-db: image: 'supabase/postgres:15.8.1.085' healthcheck: test: 'pg_isready -U postgres -h 127.0.0.1' interval: 5s timeout: 5s retries: 10 depends_on: supabase-vector: condition: service_healthy command: - postgres - '-c' - config_file=/etc/postgresql/postgresql.conf - '-c' - log_min_messages=fatal environment: - POSTGRES_HOST=/var/run/postgresql - 'PGPORT=${POSTGRES_PORT:-5432}' - 'POSTGRES_PORT=${POSTGRES_PORT:-5432}' - 'PGPASSWORD=${SERVICE_PASSWORD_POSTGRES}' - 'POSTGRES_PASSWORD=${SERVICE_PASSWORD_POSTGRES}' - 'PGDATABASE=${POSTGRES_DB:-postgres}' - 'POSTGRES_DB=${POSTGRES_DB:-postgres}' - 'JWT_SECRET=${SERVICE_PASSWORD_JWT}' - 'JWT_EXP=${JWT_EXPIRY:-3600}' volumes: - 'supabase-db-data:/var/lib/postgresql/data' - type: bind source: ./volumes/db/realtime.sql target: /docker-entrypoint-initdb.d/migrations/99-realtime.sql - type: bind source: ./volumes/db/_supabase.sql target: /docker-entrypoint-initdb.d/migrations/97-_supabase.sql - type: bind source: ./volumes/db/pooler.sql target: /docker-entrypoint-initdb.d/migrations/99-pooler.sql - type: bind source: ./volumes/db/webhooks.sql target: /docker-entrypoint-initdb.d/init-scripts/98-webhooks.sql - type: bind source: ./volumes/db/roles.sql target: /docker-entrypoint-initdb.d/init-scripts/99-roles.sql - type: bind source: ./volumes/db/jwt.sql target: /docker-entrypoint-initdb.d/init-scripts/99-jwt.sql - type: bind source: ./volumes/db/logs.sql target: /docker-entrypoint-initdb.d/migrations/99-logs.sql - 'supabase-db-config:/etc/postgresql-custom' supabase-analytics: image: 'supabase/logflare:1.22.6' healthcheck: test: - CMD - curl - 'http://127.0.0.1:4000/health' timeout: 5s interval: 5s retries: 10 depends_on: supabase-db: condition: service_healthy environment: - LOGFLARE_NODE_HOST=127.0.0.1 - DB_USERNAME=supabase_admin - DB_DATABASE=_supabase - 'DB_HOSTNAME=${POSTGRES_HOSTNAME:-supabase-db}' - 'DB_PORT=${POSTGRES_PORT:-5432}' - 'DB_PASSWORD=${SERVICE_PASSWORD_POSTGRES}' - DB_SCHEMA=_analytics - 'LOGFLARE_API_KEY=${SERVICE_PASSWORD_LOGFLARE}' - LOGFLARE_SINGLE_TENANT=true - LOGFLARE_SINGLE_TENANT_MODE=true - LOGFLARE_SUPABASE_MODE=true - LOGFLARE_MIN_CLUSTER_SIZE=1 - 'POSTGRES_BACKEND_URL=postgresql://supabase_admin:${SERVICE_PASSWORD_POSTGRES}@${POSTGRES_HOSTNAME:-supabase-db}:${POSTGRES_PORT:-5432}/_supabase' - POSTGRES_BACKEND_SCHEMA=_analytics - LOGFLARE_FEATURE_FLAG_OVERRIDE=multibackend=true supabase-vector: image: 'timberio/vector:0.28.1-alpine' healthcheck: test: - CMD - wget - '--no-verbose' - '--tries=1' - '--spider' - 'http://supabase-vector:9001/health' timeout: 5s interval: 5s retries: 3 volumes: - type: bind source: ./volumes/logs/vector.yml target: /etc/vector/vector.yml read_only: true - '/var/run/docker.sock:/var/run/docker.sock:ro' environment: - 'LOGFLARE_API_KEY=${SERVICE_PASSWORD_LOGFLARE}' command: - '--config' - etc/vector/vector.yml supabase-rest: image: 'postgrest/postgrest:v13.0.7' depends_on: supabase-db: condition: service_healthy supabase-analytics: condition: service_healthy environment: - 'PGRST_DB_URI=postgres://authenticator:${SERVICE_PASSWORD_POSTGRES}@${POSTGRES_HOSTNAME:-supabase-db}:${POSTGRES_PORT:-5432}/${POSTGRES_DB:-postgres}' - 'PGRST_DB_SCHEMAS=${PGRST_DB_SCHEMAS:-public,storage,graphql_public}' - PGRST_DB_ANON_ROLE=anon - 'PGRST_JWT_SECRET=${SERVICE_PASSWORD_JWT}' - PGRST_DB_USE_LEGACY_GUCS=false - 'PGRST_APP_SETTINGS_JWT_SECRET=${SERVICE_PASSWORD_JWT}' - 'PGRST_APP_SETTINGS_JWT_EXP=${JWT_EXPIRY:-3600}' command: postgrest exclude_from_hc: true supabase-auth: image: 'supabase/gotrue:v2.182.1' depends_on: supabase-db: condition: service_healthy supabase-analytics: condition: service_healthy healthcheck: test: - CMD - wget - '--no-verbose' - '--tries=1' - '--spider' - 'http://127.0.0.1:9999/health' timeout: 5s interval: 5s retries: 3 environment: - GOTRUE_API_HOST=0.0.0.0 - GOTRUE_API_PORT=9999 - 'API_EXTERNAL_URL=${API_EXTERNAL_URL:-http://localhost:8000}' - GOTRUE_DB_DRIVER=postgres - 'GOTRUE_DB_DATABASE_URL=postgres://supabase_auth_admin:${SERVICE_PASSWORD_POSTGRES}@${POSTGRES_HOSTNAME:-supabase-db}:${POSTGRES_PORT:-5432}/${POSTGRES_DB:-postgres}' - 'GOTRUE_SITE_URL=${SERVICE_URL_SUPABASEKONG}' - 'GOTRUE_URI_ALLOW_LIST=${ADDITIONAL_REDIRECT_URLS}' - 'GOTRUE_DISABLE_SIGNUP=${DISABLE_SIGNUP:-false}' - GOTRUE_JWT_ADMIN_ROLES=service_role - GOTRUE_JWT_AUD=authenticated - GOTRUE_JWT_DEFAULT_GROUP_NAME=authenticated - 'GOTRUE_JWT_EXP=${JWT_EXPIRY:-3600}' - 'GOTRUE_JWT_SECRET=${SERVICE_PASSWORD_JWT}' - 'GOTRUE_EXTERNAL_EMAIL_ENABLED=${ENABLE_EMAIL_SIGNUP:-true}' - 'GOTRUE_EXTERNAL_ANONYMOUS_USERS_ENABLED=${ENABLE_ANONYMOUS_USERS:-false}' - 'GOTRUE_MAILER_AUTOCONFIRM=${ENABLE_EMAIL_AUTOCONFIRM:-false}' - 'GOTRUE_SMTP_ADMIN_EMAIL=${SMTP_ADMIN_EMAIL}' - 'GOTRUE_SMTP_HOST=${SMTP_HOST}' - 'GOTRUE_SMTP_PORT=${SMTP_PORT:-587}' - 'GOTRUE_SMTP_USER=${SMTP_USER}' - 'GOTRUE_SMTP_PASS=${SMTP_PASS}' - 'GOTRUE_SMTP_SENDER_NAME=${SMTP_SENDER_NAME}' - 'GOTRUE_MAILER_URLPATHS_INVITE=${MAILER_URLPATHS_INVITE:-/auth/v1/verify}' - 'GOTRUE_MAILER_URLPATHS_CONFIRMATION=${MAILER_URLPATHS_CONFIRMATION:-/auth/v1/verify}' - 'GOTRUE_MAILER_URLPATHS_RECOVERY=${MAILER_URLPATHS_RECOVERY:-/auth/v1/verify}' - 'GOTRUE_MAILER_URLPATHS_EMAIL_CHANGE=${MAILER_URLPATHS_EMAIL_CHANGE:-/auth/v1/verify}' - 'GOTRUE_MAILER_TEMPLATES_INVITE=${MAILER_TEMPLATES_INVITE}' - 'GOTRUE_MAILER_TEMPLATES_CONFIRMATION=${MAILER_TEMPLATES_CONFIRMATION}' - 'GOTRUE_MAILER_TEMPLATES_RECOVERY=${MAILER_TEMPLATES_RECOVERY}' - 'GOTRUE_MAILER_TEMPLATES_MAGIC_LINK=${MAILER_TEMPLATES_MAGIC_LINK}' - 'GOTRUE_MAILER_TEMPLATES_EMAIL_CHANGE=${MAILER_TEMPLATES_EMAIL_CHANGE}' - 'GOTRUE_MAILER_SUBJECTS_CONFIRMATION=${MAILER_SUBJECTS_CONFIRMATION}' - 'GOTRUE_MAILER_SUBJECTS_RECOVERY=${MAILER_SUBJECTS_RECOVERY}' - 'GOTRUE_MAILER_SUBJECTS_MAGIC_LINK=${MAILER_SUBJECTS_MAGIC_LINK}' - 'GOTRUE_MAILER_SUBJECTS_EMAIL_CHANGE=${MAILER_SUBJECTS_EMAIL_CHANGE}' - 'GOTRUE_MAILER_SUBJECTS_INVITE=${MAILER_SUBJECTS_INVITE}' - 'GOTRUE_EXTERNAL_PHONE_ENABLED=${ENABLE_PHONE_SIGNUP:-true}' - 'GOTRUE_SMS_AUTOCONFIRM=${ENABLE_PHONE_AUTOCONFIRM:-true}' realtime-dev: image: 'supabase/realtime:v2.63.0' container_name: realtime-dev.supabase-realtime depends_on: supabase-db: condition: service_healthy supabase-analytics: condition: service_healthy healthcheck: test: - CMD - curl - '-sSfL' - '--head' - '-o' - /dev/null - '-H' - 'Authorization: Bearer ${SERVICE_SUPABASEANON_KEY}' - 'http://127.0.0.1:4000/api/tenants/realtime-dev/health' timeout: 5s interval: 5s retries: 3 environment: - PORT=4000 - 'DB_HOST=${POSTGRES_HOSTNAME:-supabase-db}' - 'DB_PORT=${POSTGRES_PORT:-5432}' - DB_USER=supabase_admin - 'DB_PASSWORD=${SERVICE_PASSWORD_POSTGRES}' - 'DB_NAME=${POSTGRES_DB:-postgres}' - 'DB_AFTER_CONNECT_QUERY=SET search_path TO _realtime' - DB_ENC_KEY=supabaserealtime - 'API_JWT_SECRET=${SERVICE_PASSWORD_JWT}' - FLY_ALLOC_ID=fly123 - FLY_APP_NAME=realtime - 'SECRET_KEY_BASE=${SECRET_PASSWORD_REALTIME}' - 'ERL_AFLAGS=-proto_dist inet_tcp' - ENABLE_TAILSCALE=false - "DNS_NODES=''" - RLIMIT_NOFILE=10000 - APP_NAME=realtime - SEED_SELF_HOST=true - LOG_LEVEL=error - RUN_JANITOR=true - JANITOR_INTERVAL=60000 command: "sh -c \"/app/bin/migrate && /app/bin/realtime eval 'Realtime.Release.seeds(Realtime.Repo)' && /app/bin/server\"\n" supabase-minio: image: minio/minio environment: - 'MINIO_ROOT_USER=${SERVICE_USER_MINIO}' - 'MINIO_ROOT_PASSWORD=${SERVICE_PASSWORD_MINIO}' command: 'server --console-address ":9001" /data' healthcheck: test: - CMD - mc - ready - local interval: 5s timeout: 20s retries: 10 volumes: - './volumes/storage:/data' minio-createbucket: image: minio/mc restart: 'no' environment: - 'MINIO_ROOT_USER=${SERVICE_USER_MINIO}' - 'MINIO_ROOT_PASSWORD=${SERVICE_PASSWORD_MINIO}' depends_on: supabase-minio: condition: service_healthy entrypoint: - /entrypoint.sh volumes: - type: bind source: ./entrypoint.sh target: /entrypoint.sh supabase-storage: image: 'supabase/storage-api:v1.29.0' depends_on: supabase-db: condition: service_healthy supabase-rest: condition: service_started imgproxy: condition: service_started healthcheck: test: - CMD - wget - '--no-verbose' - '--tries=1' - '--spider' - 'http://127.0.0.1:5000/status' timeout: 5s interval: 5s retries: 3 environment: - SERVER_PORT=5000 - SERVER_REGION=local - MULTI_TENANT=false - 'AUTH_JWT_SECRET=${SERVICE_PASSWORD_JWT}' - 'DATABASE_URL=postgres://supabase_storage_admin:${SERVICE_PASSWORD_POSTGRES}@${POSTGRES_HOSTNAME:-supabase-db}:${POSTGRES_PORT:-5432}/${POSTGRES_DB:-postgres}' - DB_INSTALL_ROLES=false - STORAGE_BACKEND=s3 - STORAGE_S3_BUCKET=stub - 'STORAGE_S3_ENDPOINT=http://supabase-minio:9000' - STORAGE_S3_FORCE_PATH_STYLE=true - STORAGE_S3_REGION=us-east-1 - 'AWS_ACCESS_KEY_ID=${SERVICE_USER_MINIO}' - 'AWS_SECRET_ACCESS_KEY=${SERVICE_PASSWORD_MINIO}' - UPLOAD_FILE_SIZE_LIMIT=524288000 - UPLOAD_FILE_SIZE_LIMIT_STANDARD=524288000 - UPLOAD_SIGNED_URL_EXPIRATION_TIME=120 - TUS_URL_PATH=upload/resumable - TUS_MAX_SIZE=3600000 - ENABLE_IMAGE_TRANSFORMATION=true - 'IMGPROXY_URL=http://imgproxy:8080' - IMGPROXY_REQUEST_TIMEOUT=15 - DATABASE_SEARCH_PATH=storage - NODE_ENV=production - REQUEST_ALLOW_X_FORWARDED_PATH=true volumes: - './volumes/storage:/var/lib/storage' imgproxy: image: 'darthsim/imgproxy:v3.8.0' healthcheck: test: - CMD - imgproxy - health timeout: 5s interval: 5s retries: 3 environment: - IMGPROXY_LOCAL_FILESYSTEM_ROOT=/ - IMGPROXY_USE_ETAG=true - 'IMGPROXY_ENABLE_WEBP_DETECTION=${IMGPROXY_ENABLE_WEBP_DETECTION:-true}' volumes: - './volumes/storage:/var/lib/storage' supabase-meta: image: 'supabase/postgres-meta:v0.93.1' depends_on: supabase-db: condition: service_healthy supabase-analytics: condition: service_healthy environment: - PG_META_PORT=8080 - 'PG_META_DB_HOST=${POSTGRES_HOSTNAME:-supabase-db}' - 'PG_META_DB_PORT=${POSTGRES_PORT:-5432}' - 'PG_META_DB_NAME=${POSTGRES_DB:-postgres}' - PG_META_DB_USER=supabase_admin - 'PG_META_DB_PASSWORD=${SERVICE_PASSWORD_POSTGRES}' supabase-edge-functions: image: 'supabase/edge-runtime:v1.69.23' depends_on: supabase-analytics: condition: service_healthy healthcheck: test: - CMD - echo - 'Edge Functions is healthy' timeout: 5s interval: 5s retries: 3 environment: - 'JWT_SECRET=${SERVICE_PASSWORD_JWT}' - 'SUPABASE_URL=${SERVICE_URL_SUPABASEKONG}' - 'SUPABASE_ANON_KEY=${SERVICE_SUPABASEANON_KEY}' - 'SUPABASE_SERVICE_ROLE_KEY=${SERVICE_SUPABASESERVICE_KEY}' - 'SUPABASE_DB_URL=postgresql://postgres:${SERVICE_PASSWORD_POSTGRES}@${POSTGRES_HOSTNAME:-supabase-db}:${POSTGRES_PORT:-5432}/${POSTGRES_DB:-postgres}' - 'VERIFY_JWT=${FUNCTIONS_VERIFY_JWT:-false}' volumes: - './volumes/functions:/home/deno/functions' - type: bind source: ./volumes/functions/main/index.ts target: /home/deno/functions/main/index.ts - type: bind source: ./volumes/functions/hello/index.ts target: /home/deno/functions/hello/index.ts command: - start - '--main-service' - /home/deno/functions/main supabase-supavisor: image: 'supabase/supavisor:2.7.4' healthcheck: test: - CMD - curl - '-sSfL' - '-o' - /dev/null - 'http://127.0.0.1:4000/api/health' timeout: 5s interval: 5s retries: 10 depends_on: supabase-db: condition: service_healthy supabase-analytics: condition: service_healthy environment: - POOLER_TENANT_ID=dev_tenant - POOLER_POOL_MODE=transaction - 'POOLER_DEFAULT_POOL_SIZE=${POOLER_DEFAULT_POOL_SIZE:-20}' - 'POOLER_MAX_CLIENT_CONN=${POOLER_MAX_CLIENT_CONN:-100}' - PORT=4000 - 'POSTGRES_PORT=${POSTGRES_PORT:-5432}' - 'POSTGRES_HOSTNAME=${POSTGRES_HOSTNAME:-supabase-db}' - 'POSTGRES_DB=${POSTGRES_DB:-postgres}' - 'POSTGRES_PASSWORD=${SERVICE_PASSWORD_POSTGRES}' - 'DATABASE_URL=ecto://supabase_admin:${SERVICE_PASSWORD_POSTGRES}@${POSTGRES_HOSTNAME:-supabase-db}:${POSTGRES_PORT:-5432}/_supabase' - CLUSTER_POSTGRES=true - 'SECRET_KEY_BASE=${SERVICE_PASSWORD_SUPAVISORSECRET}' - 'VAULT_ENC_KEY=${SERVICE_PASSWORD_VAULTENC}' - 'API_JWT_SECRET=${SERVICE_PASSWORD_JWT}' - 'METRICS_JWT_SECRET=${SERVICE_PASSWORD_JWT}' - REGION=local - 'ERL_AFLAGS=-proto_dist inet_tcp' command: - /bin/sh - '-c' - '/app/bin/migrate && /app/bin/supavisor eval "$$(cat /etc/pooler/pooler.exs)" && /app/bin/server' volumes: - type: bind source: ./volumes/pooler/pooler.exs target: /etc/pooler/pooler.exs