{
  "schema_version": 1,
  "generated_at": "2026-10-23T00:00:00.000Z",
  "canonical_url": "https://openlogs.fyi/tools/supabase",
  "slug": "supabase",
  "name": "Supabase",
  "tagline": "Postgres-based backend with authentication, storage, realtime and edge functions.",
  "replaces": {
    "summary": "Paid backend-as-a-service services, such as Firebase.",
    "products": [
      {
        "slug": "firebase",
        "name": "Firebase",
        "vendor": "Google",
        "url": "https://openlogs.fyi/alternatives/firebase"
      }
    ]
  },
  "stack": [
    {
      "kind": "language",
      "name": "TypeScript"
    },
    {
      "kind": "database",
      "name": "PostgreSQL"
    }
  ],
  "deployment_methods": [
    "docker_compose"
  ],
  "requirements": {
    "min_ram_mb": null,
    "min_cpu_cores": null,
    "source_url": null
  },
  "license": {
    "spdx": "Apache-2.0",
    "class": "osi",
    "source_url": "https://github.com/supabase/supabase/blob/8ba2f0a63bc6d9cc00886557ffa027c5e8a83cfc/LICENSE"
  },
  "business_model": {
    "value": "unknown",
    "notes": "The README at the pinned commit does not state a business model, so it stays unknown.",
    "source_url": "https://github.com/supabase/supabase/blob/8ba2f0a63bc6d9cc00886557ffa027c5e8a83cfc/README.md",
    "checked_at": "2026-10-22"
  },
  "difficulty": {
    "value": "hard",
    "reason": "11 services (1 database or broker: postgres), 19 volume mounts, 51 env values to set, no elevated host access. Heuristic score 8 of 11: hard. A source checkout is needed, so the rating is at least medium."
  },
  "repository": {
    "owner": "supabase",
    "name": "supabase",
    "url": "https://github.com/supabase/supabase"
  },
  "compose_check": {
    "status": "config_ok",
    "validated_at": "2026-10-22",
    "summary": "config_ok on 2026-10-22"
  },
  "recipe": {
    "version": 1,
    "source_path": "docker/docker-compose.yml",
    "source_repo": "supabase/supabase",
    "source_commit_sha": "8ba2f0a63bc6d9cc00886557ffa027c5e8a83cfc",
    "source_kind": "app_repo",
    "source_url": "https://github.com/supabase/supabase/blob/8ba2f0a63bc6d9cc00886557ffa027c5e8a83cfc/docker/docker-compose.yml"
  },
  "install_steps": [
    {
      "order": 1,
      "kind": "prerequisite",
      "language": "text",
      "label": "Docker with the Compose plugin and git",
      "body": "A host with Docker, the Docker Compose plugin and git. This entry needs a checkout of the project repository.",
      "source_url": "https://github.com/supabase/supabase/blob/8ba2f0a63bc6d9cc00886557ffa027c5e8a83cfc/apps/docs/content/guides/self-hosting/docker.mdx",
      "validation_status": "unvalidated"
    },
    {
      "order": 2,
      "kind": "shell",
      "language": "bash",
      "label": "Get the source at commit 8ba2f0a",
      "body": "git init supabase\ncd supabase\ngit remote add origin https://github.com/supabase/supabase.git\ngit sparse-checkout init --cone\ngit sparse-checkout set docker\ngit fetch --depth 1 --filter=blob:none origin 8ba2f0a63bc6d9cc00886557ffa027c5e8a83cfc\ngit checkout FETCH_HEAD",
      "source_url": "https://github.com/supabase/supabase/blob/8ba2f0a63bc6d9cc00886557ffa027c5e8a83cfc/apps/docs/content/guides/self-hosting/docker.mdx",
      "validation_status": "unvalidated"
    },
    {
      "order": 3,
      "kind": "shell",
      "language": "bash",
      "label": "Go to the folder the docs use",
      "body": "cd docker",
      "source_url": "https://github.com/supabase/supabase/blob/8ba2f0a63bc6d9cc00886557ffa027c5e8a83cfc/apps/docs/content/guides/self-hosting/docker.mdx",
      "validation_status": "unvalidated"
    },
    {
      "order": 4,
      "kind": "compose",
      "language": "yaml",
      "label": "The compose file in the checkout: docker/docker-compose.yml (shown here for reading)",
      "body": "# Usage\n#   Start:              docker compose up -d\n#   Stop:               docker compose down\n#   Dev mode:           docker compose -f docker-compose.yml -f ./dev/docker-compose.dev.yml up -d\n#   Reset everything:   sh reset.sh\n#\n# Notes:\n#   - Nested variable interpolation (${A:-${B}}) requires podman-compose >= 1.6.0\n#\n\nname: supabase\n\nservices:\n\n  studio:\n    container_name: supabase-studio\n    image: supabase/studio:2026.10.12-sha-3699278\n    restart: unless-stopped\n    healthcheck:\n      test:\n        [\n          \"CMD-SHELL\",\n          \"node -e \\\"fetch('http://localhost:3000/api/platform/profile').then((r) => {if (r.status !== 200) throw new Error(r.status)})\\\"\"\n        ]\n      timeout: 10s\n      interval: 5s\n      retries: 3\n      start_period: 20s\n    environment:\n      # Listen on all IPv4 interfaces\n      HOSTNAME: \"0.0.0.0\"\n\n      STUDIO_PG_META_URL: http://meta:8080\n      POSTGRES_PORT: ${POSTGRES_PORT}\n      POSTGRES_HOST: ${POSTGRES_HOST}\n      POSTGRES_DB: ${POSTGRES_DB}\n      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}\n\n      # See: https://supabase.com/docs/guides/self-hosting/remove-superuser-access\n      POSTGRES_USER_READ_WRITE: postgres\n\n      PG_META_CRYPTO_KEY: ${PG_META_CRYPTO_KEY}\n      PGRST_DB_SCHEMAS: ${PGRST_DB_SCHEMAS}\n      PGRST_DB_MAX_ROWS: ${PGRST_DB_MAX_ROWS:-1000}\n      PGRST_DB_EXTRA_SEARCH_PATH: ${PGRST_DB_EXTRA_SEARCH_PATH:-public}\n\n      DEFAULT_ORGANIZATION_NAME: ${STUDIO_DEFAULT_ORGANIZATION}\n      DEFAULT_PROJECT_NAME: ${STUDIO_DEFAULT_PROJECT}\n      OPENAI_API_KEY: ${OPENAI_API_KEY}\n\n      SUPABASE_URL: http://api-gw:8000\n      SUPABASE_PUBLIC_URL: ${SUPABASE_PUBLIC_URL}\n      SUPABASE_ANON_KEY: ${ANON_KEY}\n      SUPABASE_SERVICE_KEY: ${SERVICE_ROLE_KEY}\n      AUTH_JWT_SECRET: ${JWT_SECRET}\n      SUPABASE_PUBLISHABLE_KEY: ${SUPABASE_PUBLISHABLE_KEY}\n      SUPABASE_SECRET_KEY: ${SUPABASE_SECRET_KEY}\n\n      # See: docker-compose.logs.yml\n      ENABLED_FEATURES_LOGS_ALL: \"false\"\n\n      SNIPPETS_MANAGEMENT_FOLDER: /app/snippets\n      EDGE_FUNCTIONS_MANAGEMENT_FOLDER: /app/edge-functions\n    volumes:\n      - ./volumes/snippets:/app/snippets:z\n      - ./volumes/functions:/app/edge-functions:ro,z\n\n  # Envoy is the default API gateway\n  # See: https://github.com/orgs/supabase/discussions/48048\n  api-gw:\n    container_name: supabase-envoy\n    image: envoyproxy/envoy:v1.39.1\n    restart: unless-stopped\n    networks:\n      default:\n        # Expose `envoy` and `kong` as network aliases, so internal configs\n        # that reference either hostname resolve to whichever gateway is active.\n        aliases:\n          - envoy\n          - kong\n    healthcheck:\n      # Using a TCP port check because this image does not include curl or wget.\n      test: [\"CMD-SHELL\", \"timeout 1 bash -c '</dev/tcp/127.0.0.1/8000'\"]\n      interval: 10s\n      timeout: 5s\n      retries: 3\n    depends_on:\n      studio:\n        condition: service_healthy\n    ports:\n      - ${API_GW_HTTP_PORT:-${KONG_HTTP_PORT:-8000}}:8000/tcp\n    volumes:\n      - ./volumes/api/envoy/envoy.yaml:/etc/envoy/envoy.yaml:ro\n      - ./volumes/api/envoy/cds.yaml:/etc/envoy/cds.yaml:ro\n      - ./volumes/api/envoy/lds.template.yaml:/etc/envoy/lds.template.yaml:ro\n      - ./volumes/api/envoy/docker-entrypoint.sh:/docker-entrypoint.sh:ro\n    environment:\n      ANON_KEY: ${ANON_KEY}\n      SERVICE_ROLE_KEY: ${SERVICE_ROLE_KEY}\n      SUPABASE_PUBLISHABLE_KEY: ${SUPABASE_PUBLISHABLE_KEY:-}\n      SUPABASE_SECRET_KEY: ${SUPABASE_SECRET_KEY:-}\n      ANON_KEY_ASYMMETRIC: ${ANON_KEY_ASYMMETRIC:-}\n      SERVICE_ROLE_KEY_ASYMMETRIC: ${SERVICE_ROLE_KEY_ASYMMETRIC:-}\n      SUPABASE_PUBLIC_URL: ${SUPABASE_PUBLIC_URL}\n      DASHBOARD_USERNAME: ${DASHBOARD_USERNAME}\n      DASHBOARD_PASSWORD: ${DASHBOARD_PASSWORD}\n    entrypoint: [\"/bin/sh\", \"/docker-entrypoint.sh\"]\n\n  auth:\n    container_name: supabase-auth\n    image: supabase/gotrue:v2.197.0\n    restart: unless-stopped\n    healthcheck:\n      test:\n        [\n          \"CMD\",\n          \"wget\",\n          \"--no-verbose\",\n          \"--tries=1\",\n          \"--spider\",\n          \"http://localhost:9999/health\"\n        ]\n      timeout: 5s\n      interval: 5s\n      retries: 3\n    depends_on:\n      db:\n        # Disable this if you are using an external Postgres database\n        condition: service_healthy\n    environment:\n      GOTRUE_API_HOST: 0.0.0.0\n      GOTRUE_API_PORT: 9999\n      API_EXTERNAL_URL: ${API_EXTERNAL_URL}\n\n      GOTRUE_DB_DRIVER: postgres\n      GOTRUE_DB_DATABASE_URL: postgres://supabase_auth_admin:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}\n\n      GOTRUE_SITE_URL: ${SITE_URL}\n      GOTRUE_URI_ALLOW_LIST: ${ADDITIONAL_REDIRECT_URLS}\n      GOTRUE_DISABLE_SIGNUP: ${DISABLE_SIGNUP}\n\n      GOTRUE_JWT_ADMIN_ROLES: service_role\n      GOTRUE_JWT_AUD: authenticated\n      GOTRUE_JWT_DEFAULT_GROUP_NAME: authenticated\n      GOTRUE_JWT_EXP: ${JWT_EXPIRY}\n\n      # Legacy symmetric HS256 key\n      GOTRUE_JWT_SECRET: ${JWT_SECRET}\n\n      # JSON array of signing JWKs (EC private + legacy symmetric)\n      # For Podman, use: GOTRUE_JWT_KEYS: ${JWT_KEYS}\n      #GOTRUE_JWT_KEYS: ${JWT_KEYS:-[]}\n\n      GOTRUE_JWT_ISSUER: ${API_EXTERNAL_URL}\n\n      GOTRUE_EXTERNAL_EMAIL_ENABLED: ${ENABLE_EMAIL_SIGNUP}\n      GOTRUE_EXTERNAL_ANONYMOUS_USERS_ENABLED: ${ENABLE_ANONYMOUS_USERS}\n      GOTRUE_MAILER_AUTOCONFIRM: ${ENABLE_EMAIL_AUTOCONFIRM}\n\n      # Uncomment to bypass nonce check in ID Token flow. Commonly set to true when using Google Sign In on mobile.\n      # GOTRUE_EXTERNAL_SKIP_NONCE_CHECK: \"true\"\n\n      # GOTRUE_MAILER_SECURE_EMAIL_CHANGE_ENABLED: \"true\"\n      # GOTRUE_SMTP_MAX_FREQUENCY: 1s\n      GOTRUE_SMTP_ADMIN_EMAIL: ${SMTP_ADMIN_EMAIL}\n      GOTRUE_SMTP_HOST: ${SMTP_HOST}\n      GOTRUE_SMTP_PORT: ${SMTP_PORT}\n      GOTRUE_SMTP_USER: ${SMTP_USER}\n      GOTRUE_SMTP_PASS: ${SMTP_PASS}\n      GOTRUE_SMTP_SENDER_NAME: ${SMTP_SENDER_NAME}\n      GOTRUE_MAILER_URLPATHS_INVITE: ${MAILER_URLPATHS_INVITE}\n      GOTRUE_MAILER_URLPATHS_CONFIRMATION: ${MAILER_URLPATHS_CONFIRMATION}\n      GOTRUE_MAILER_URLPATHS_RECOVERY: ${MAILER_URLPATHS_RECOVERY}\n      GOTRUE_MAILER_URLPATHS_EMAIL_CHANGE: ${MAILER_URLPATHS_EMAIL_CHANGE}\n\n      GOTRUE_EXTERNAL_PHONE_ENABLED: ${ENABLE_PHONE_SIGNUP}\n      GOTRUE_SMS_AUTOCONFIRM: ${ENABLE_PHONE_AUTOCONFIRM}\n\n      # Uncomment to enable OAuth / social login providers.\n      # GOTRUE_EXTERNAL_GOOGLE_ENABLED: ${GOOGLE_ENABLED}\n      # GOTRUE_EXTERNAL_GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID}\n      # GOTRUE_EXTERNAL_GOOGLE_SECRET: ${GOOGLE_SECRET}\n      # GOTRUE_EXTERNAL_GOOGLE_REDIRECT_URI: ${API_EXTERNAL_URL}/callback\n\n      # GOTRUE_EXTERNAL_GITHUB_ENABLED: ${GITHUB_ENABLED}\n      # GOTRUE_EXTERNAL_GITHUB_CLIENT_ID: ${GITHUB_CLIENT_ID}\n      # GOTRUE_EXTERNAL_GITHUB_SECRET: ${GITHUB_SECRET}\n      # GOTRUE_EXTERNAL_GITHUB_REDIRECT_URI: ${API_EXTERNAL_URL}/callback\n\n      # GOTRUE_EXTERNAL_AZURE_ENABLED: ${AZURE_ENABLED}\n      # GOTRUE_EXTERNAL_AZURE_CLIENT_ID: ${AZURE_CLIENT_ID}\n      # GOTRUE_EXTERNAL_AZURE_SECRET: ${AZURE_SECRET}\n      # GOTRUE_EXTERNAL_AZURE_REDIRECT_URI: ${API_EXTERNAL_URL}/callback\n\n      # Uncomment to configure SMS delivery (phone auth and phone MFA).\n      # GOTRUE_SMS_PROVIDER: ${SMS_PROVIDER}\n      # GOTRUE_SMS_OTP_EXP: ${SMS_OTP_EXP}\n      # GOTRUE_SMS_OTP_LENGTH: ${SMS_OTP_LENGTH}\n      # GOTRUE_SMS_MAX_FREQUENCY: ${SMS_MAX_FREQUENCY}\n      # GOTRUE_SMS_TEMPLATE: ${SMS_TEMPLATE}\n\n      # Twilio credentials (when SMS_PROVIDER=twilio)\n      # GOTRUE_SMS_TWILIO_ACCOUNT_SID: ${SMS_TWILIO_ACCOUNT_SID}\n      # GOTRUE_SMS_TWILIO_AUTH_TOKEN: ${SMS_TWILIO_AUTH_TOKEN}\n      # GOTRUE_SMS_TWILIO_MESSAGE_SERVICE_SID: ${SMS_TWILIO_MESSAGE_SERVICE_SID}\n\n      # Test OTP mappings for development\n      # GOTRUE_SMS_TEST_OTP: ${SMS_TEST_OTP}\n\n      # Uncomment to configure multi-factor authentication (MFA).\n      # GOTRUE_MFA_TOTP_ENROLL_ENABLED: ${MFA_TOTP_ENROLL_ENABLED}\n      # GOTRUE_MFA_TOTP_VERIFY_ENABLED: ${MFA_TOTP_VERIFY_ENABLED}\n      # GOTRUE_MFA_PHONE_ENROLL_ENABLED: ${MFA_PHONE_ENROLL_ENABLED}\n      # GOTRUE_MFA_PHONE_VERIFY_ENABLED: ${MFA_PHONE_VERIFY_ENABLED}\n      # GOTRUE_MFA_MAX_ENROLLED_FACTORS: ${MFA_MAX_ENROLLED_FACTORS}\n\n      # SAML SSO\n      # See: https://supabase.com/docs/guides/self-hosting/self-hosted-saml-sso\n      # GOTRUE_SAML_ENABLED: ${SAML_ENABLED}\n      # GOTRUE_SAML_PRIVATE_KEY: ${SAML_PRIVATE_KEY}\n      # GOTRUE_SAML_ALLOW_ENCRYPTED_ASSERTIONS: ${SAML_ALLOW_ENCRYPTED_ASSERTIONS}\n      # GOTRUE_SAML_RELAY_STATE_VALIDITY_PERIOD: ${SAML_RELAY_STATE_VALIDITY_PERIOD}\n      # GOTRUE_SAML_RATE_LIMIT_ASSERTION: ${SAML_RATE_LIMIT_ASSERTION}\n      # Optional, defaults to API_EXTERNAL_URL if not set\n      # GOTRUE_SAML_EXTERNAL_URL: ${SAML_EXTERNAL_URL}\n\n      # Uncomment to enable custom access token hook.\n      # See: https://supabase.com/docs/guides/auth/auth-hooks for\n      # full list of hooks and additional details about custom_access_token_hook\n\n      # GOTRUE_HOOK_CUSTOM_ACCESS_TOKEN_ENABLED: \"true\"\n      # GOTRUE_HOOK_CUSTOM_ACCESS_TOKEN_URI: \"pg-functions://postgres/public/custom_access_token_hook\"\n      # GOTRUE_HOOK_CUSTOM_ACCESS_TOKEN_SECRETS: \"<standard-base64-secret>\"\n\n      # GOTRUE_HOOK_MFA_VERIFICATION_ATTEMPT_ENABLED: \"true\"\n      # GOTRUE_HOOK_MFA_VERIFICATION_ATTEMPT_URI: \"pg-functions://postgres/public/mfa_verification_attempt\"\n\n      # GOTRUE_HOOK_PASSWORD_VERIFICATION_ATTEMPT_ENABLED: \"true\"\n      # GOTRUE_HOOK_PASSWORD_VERIFICATION_ATTEMPT_URI: \"pg-functions://postgres/public/password_verification_attempt\"\n\n      # GOTRUE_HOOK_SEND_SMS_ENABLED: \"false\"\n      # GOTRUE_HOOK_SEND_SMS_URI: \"pg-functions://postgres/public/custom_access_token_hook\"\n      # GOTRUE_HOOK_SEND_SMS_SECRETS: \"v1,whsec_VGhpcyBpcyBhbiBleGFtcGxlIG9mIGEgc2hvcnRlciBCYXNlNjQgc3RyaW5n\"\n\n      # GOTRUE_HOOK_SEND_EMAIL_ENABLED: \"false\"\n      # GOTRUE_HOOK_SEND_EMAIL_URI: \"http://host.docker.internal:54321/functions/v1/email_sender\"\n      # GOTRUE_HOOK_SEND_EMAIL_SECRETS: \"v1,whsec_VGhpcyBpcyBhbiBleGFtcGxlIG9mIGEgc2hvcnRlciBCYXNlNjQgc3RyaW5n\"\n\n  rest:\n    container_name: supabase-rest\n    image: postgrest/postgrest:v14.18\n    restart: unless-stopped\n    depends_on:\n      db:\n        # Disable this if you are using an external Postgres database\n        condition: service_healthy\n    healthcheck:\n      test: [ \"CMD\", \"postgrest\", \"--ready\" ]\n      interval: 5s\n      timeout: 5s\n      retries: 3\n    environment:\n      PGRST_DB_URI: postgres://authenticator:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}\n      PGRST_DB_SCHEMAS: ${PGRST_DB_SCHEMAS}\n      PGRST_DB_MAX_ROWS: ${PGRST_DB_MAX_ROWS:-1000}\n      PGRST_DB_EXTRA_SEARCH_PATH: ${PGRST_DB_EXTRA_SEARCH_PATH:-public}\n      PGRST_DB_ANON_ROLE: anon\n\n      PGRST_ADMIN_SERVER_PORT: 3001\n      PGRST_ADMIN_SERVER_HOST: localhost\n\n      # PostgREST accepts a plain-text symmetric secret, a single JWK, or a JWKS.\n      # For Podman, use either PGRST_JWT_SECRET: ${JWT_SECRET} or\n      # PGRST_JWT_SECRET: ${JWT_JWKS}\n      PGRST_JWT_SECRET: ${JWT_JWKS:-${JWT_SECRET}}\n\n      PGRST_DB_USE_LEGACY_GUCS: \"false\"\n      PGRST_APP_SETTINGS_JWT_EXP: ${JWT_EXPIRY}\n    command:\n      [\n        \"postgrest\"\n      ]\n\n  realtime:\n    # This container name looks inconsistent but is correct because realtime constructs tenant id by parsing the subdomain\n    container_name: realtime-dev.supabase-realtime\n    image: supabase/realtime:v2.143.3\n    restart: unless-stopped\n    depends_on:\n      db:\n        # Disable this if you are using an external Postgres database\n        condition: service_healthy\n    healthcheck:\n      test:\n        [\n          \"CMD-SHELL\",\n          \"curl -sSfL --head -o /dev/null -H \\\"Authorization: Bearer ${ANON_KEY}\\\" http://localhost:4000/api/tenants/realtime-dev/health\"\n        ]\n      timeout: 5s\n      interval: 30s\n      retries: 3\n      start_period: 10s\n    environment:\n      PORT: 4000\n      DB_HOST: ${POSTGRES_HOST}\n      DB_PORT: ${POSTGRES_PORT}\n      DB_USER: supabase_admin\n      DB_PASSWORD: ${POSTGRES_PASSWORD}\n      DB_NAME: ${POSTGRES_DB}\n      DB_AFTER_CONNECT_QUERY: 'SET search_path TO _realtime'\n      DB_ENC_KEY: ${REALTIME_DB_ENC_KEY:-supabaserealtime}\n\n      # Legacy symmetric HS256 key\n      API_JWT_SECRET: ${JWT_SECRET}\n\n      # JWKS for token verification (EC public + legacy symmetric).\n      # For Podman, use: API_JWT_JWKS: ${JWT_JWKS}\n      #API_JWT_JWKS: ${JWT_JWKS:-{\"keys\":[]}}\n\n      SECRET_KEY_BASE: ${SECRET_KEY_BASE}\n      METRICS_JWT_SECRET: ${JWT_SECRET}\n      ERL_AFLAGS: -proto_dist inet_tcp\n      DNS_NODES: \"''\"\n      RLIMIT_NOFILE: \"10000\"\n      APP_NAME: realtime\n      SEED_SELF_HOST: \"true\"\n      RUN_JANITOR: \"true\"\n      DISABLE_HEALTHCHECK_LOGGING: \"true\"\n\n  # To use S3 backed storage: docker compose -f docker-compose.yml -f docker-compose.s3.yml up\n  storage:\n    container_name: supabase-storage\n    image: supabase/storage-api:v1.81.0\n    restart: unless-stopped\n    depends_on:\n      db:\n        # Disable this if you are using an external Postgres database\n        condition: service_healthy\n      rest:\n        condition: service_started\n      imgproxy:\n        condition: service_started\n    healthcheck:\n      test:\n        [\n          \"CMD\",\n          \"wget\",\n          \"--no-verbose\",\n          \"--tries=1\",\n          \"--spider\",\n          \"http://storage:5000/status\"\n        ]\n      timeout: 5s\n      interval: 5s\n      retries: 3\n      start_period: 10s\n    environment:\n      ANON_KEY: ${ANON_KEY}\n      SERVICE_KEY: ${SERVICE_ROLE_KEY}\n      POSTGREST_URL: http://rest:3000\n\n      # Legacy symmetric HS256 key\n      AUTH_JWT_SECRET: ${JWT_SECRET}\n\n      # JWKS for token verification (EC public + legacy symmetric).\n      # For Podman, use: JWT_JWKS: ${JWT_JWKS}\n      #JWT_JWKS: ${JWT_JWKS:-{\"keys\":[]}}\n\n      DATABASE_URL: postgres://supabase_storage_admin:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}\n      STORAGE_PUBLIC_URL: ${SUPABASE_PUBLIC_URL}\n      REQUEST_ALLOW_X_FORWARDED_PATH: \"true\"\n      FILE_SIZE_LIMIT: 52428800\n      STORAGE_BACKEND: file\n      # S3 bucket when using S3 backend, directory name when using 'file'\n      GLOBAL_S3_BUCKET: ${GLOBAL_S3_BUCKET}\n      # S3 Backend configuration\n      #GLOBAL_S3_ENDPOINT: https://your-s3-endpoint\n      #GLOBAL_S3_PROTOCOL: https\n      #GLOBAL_S3_FORCE_PATH_STYLE: \"true\"\n      #AWS_ACCESS_KEY_ID: your-access-key-id\n      #AWS_SECRET_ACCESS_KEY: your-secret-access-key\n      FILE_STORAGE_BACKEND_PATH: /var/lib/storage\n      TENANT_ID: ${STORAGE_TENANT_ID}\n      # TODO: https://github.com/supabase/storage-api/issues/55\n      REGION: ${REGION}\n      ENABLE_IMAGE_TRANSFORMATION: \"true\"\n      IMGPROXY_URL: http://imgproxy:5001\n      # S3 protocol endpoint configuration\n      S3_PROTOCOL_ACCESS_KEY_ID: ${S3_PROTOCOL_ACCESS_KEY_ID}\n      S3_PROTOCOL_ACCESS_KEY_SECRET: ${S3_PROTOCOL_ACCESS_KEY_SECRET}\n    volumes:\n      - ./volumes/storage:/var/lib/storage:z\n\n  imgproxy:\n    container_name: supabase-imgproxy\n    image: darthsim/imgproxy:v3.31.4\n    restart: unless-stopped\n    volumes:\n      - ./volumes/storage:/var/lib/storage:z\n    healthcheck:\n      test:\n        [\n          \"CMD\",\n          \"imgproxy\",\n          \"health\"\n        ]\n      timeout: 5s\n      interval: 5s\n      retries: 3\n    environment:\n      IMGPROXY_BIND: \":5001\"\n      IMGPROXY_LOCAL_FILESYSTEM_ROOT: /\n      IMGPROXY_USE_ETAG: \"true\"\n      IMGPROXY_AUTO_WEBP: ${IMGPROXY_AUTO_WEBP}\n      IMGPROXY_MAX_SRC_RESOLUTION: 16.8\n\n  meta:\n    container_name: supabase-meta\n    image: supabase/postgres-meta:v0.100.0\n    restart: unless-stopped\n    depends_on:\n      db:\n        # Disable this if you are using an external Postgres database\n        condition: service_healthy\n    environment:\n      PG_META_PORT: 8080\n      PG_META_DB_HOST: ${POSTGRES_HOST}\n      PG_META_DB_PORT: ${POSTGRES_PORT}\n      PG_META_DB_NAME: ${POSTGRES_DB}\n      PG_META_DB_USER: postgres\n      PG_META_DB_PASSWORD: ${POSTGRES_PASSWORD}\n      CRYPTO_KEY: ${PG_META_CRYPTO_KEY}\n\n  functions:\n    container_name: supabase-edge-functions\n    image: supabase/edge-runtime:v1.77.4\n    restart: unless-stopped\n    volumes:\n      - ./volumes/functions:/home/deno/functions:z\n      - deno-cache:/root/.cache/deno\n    depends_on:\n      api-gw:\n        condition: service_healthy\n    healthcheck:\n      test: [\"CMD-SHELL\", \"timeout 1 bash -c '</dev/tcp/127.0.0.1/9000'\"]\n      interval: 5s\n      timeout: 5s\n      retries: 3\n    environment:\n      # Legacy symmetric HS256 key\n      JWT_SECRET: ${JWT_SECRET}\n      # JWKS for token verification (EC public + legacy symmetric).\n      # For Podman, use: SUPABASE_JWKS: ${JWT_JWKS}\n      #SUPABASE_JWKS: ${JWT_JWKS:-{\"keys\":[]}}\n\n      SUPABASE_URL: http://api-gw:8000\n      SUPABASE_PUBLIC_URL: ${SUPABASE_PUBLIC_URL}\n      # Legacy API keys (HS256-signed JWTs)\n      SUPABASE_ANON_KEY: ${ANON_KEY}\n      SUPABASE_SERVICE_ROLE_KEY: ${SERVICE_ROLE_KEY}\n      # New opaque API keys\n      SUPABASE_PUBLISHABLE_KEYS: \"{\\\"default\\\":\\\"${SUPABASE_PUBLISHABLE_KEY:-}\\\"}\"\n      SUPABASE_SECRET_KEYS: \"{\\\"default\\\":\\\"${SUPABASE_SECRET_KEY:-}\\\"}\"\n      SUPABASE_DB_URL: postgresql://postgres:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}\n      # TODO: Allow configuring VERIFY_JWT per function.\n      VERIFY_JWT: \"${FUNCTIONS_VERIFY_JWT}\"\n    command:\n      [\n        \"start\",\n        \"--user-worker-request-idle-timeout\",\n        \"150000\",\n        \"--main-service\",\n        \"/home/deno/functions/main\"\n      ]\n\n  # Comment out everything below this point if you are using an external Postgres database\n  db:\n    container_name: supabase-db\n    # To upgrade an existing Postgres 15 database in place, see utils/upgrade-pg17.sh.\n    image: supabase/postgres:17.6.1.136\n    restart: unless-stopped\n    volumes:\n      - ./volumes/db/realtime.sql:/docker-entrypoint-initdb.d/migrations/99-realtime.sql:Z\n      # Must be superuser to create event trigger\n      - ./volumes/db/webhooks.sql:/docker-entrypoint-initdb.d/init-scripts/98-webhooks.sql:Z\n      # Must be superuser to alter reserved role\n      - ./volumes/db/roles.sql:/docker-entrypoint-initdb.d/init-scripts/99-roles.sql:Z\n      # Initialize the database settings with JWT_EXP\n      - ./volumes/db/jwt.sql:/docker-entrypoint-initdb.d/init-scripts/99-jwt.sql:Z\n      # PGDATA directory is persisted between restarts\n      - ./volumes/db/data:/var/lib/postgresql/data:Z\n      # Changes required for internal supabase data such as _analytics\n      - ./volumes/db/_supabase.sql:/docker-entrypoint-initdb.d/migrations/97-_supabase.sql:Z\n      # Changes required for Analytics support\n      - ./volumes/db/logs.sql:/docker-entrypoint-initdb.d/migrations/99-logs.sql:Z\n      # Changes required for Pooler support\n      - ./volumes/db/pooler.sql:/docker-entrypoint-initdb.d/migrations/99-pooler.sql:Z\n      # Use named volume to persist pgsodium decryption key between restarts\n      - db-config:/etc/postgresql-custom\n    healthcheck:\n      test:\n        [\n        \"CMD\",\n        \"pg_isready\",\n        \"-U\",\n        \"postgres\",\n        \"-h\",\n        \"localhost\"\n        ]\n      interval: 5s\n      timeout: 5s\n      retries: 10\n    environment:\n      POSTGRES_HOST: /var/run/postgresql\n      PGPORT: ${POSTGRES_PORT}\n      POSTGRES_PORT: ${POSTGRES_PORT}\n      PGPASSWORD: ${POSTGRES_PASSWORD}\n      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}\n      PGDATABASE: ${POSTGRES_DB}\n      POSTGRES_DB: ${POSTGRES_DB}\n      JWT_EXP: ${JWT_EXPIRY}\n    command:\n      [\n        \"postgres\",\n        \"-c\",\n        \"config_file=/etc/postgresql/postgresql.conf\",\n        \"-c\",\n        \"log_min_messages=fatal\" # prevents Realtime polling queries from appearing in logs\n      ]\n\n  # Update the DATABASE_URL if you are using an external Postgres database\n  supavisor:\n    container_name: supabase-pooler\n    image: supabase/supavisor:2.9.13\n    restart: unless-stopped\n    ports:\n      - ${POSTGRES_PORT}:5432\n      - ${POOLER_PROXY_PORT_TRANSACTION}:6543\n    volumes:\n      - ./volumes/pooler/pooler.exs:/etc/pooler/pooler.exs:ro,z\n    healthcheck:\n      test:\n        [\n          \"CMD\",\n          \"curl\",\n          \"-sSfL\",\n          \"--head\",\n          \"-o\",\n          \"/dev/null\",\n          \"http://127.0.0.1:4000/api/health\"\n        ]\n      interval: 10s\n      timeout: 5s\n      retries: 10\n      start_period: 30s\n    depends_on:\n      db:\n        condition: service_healthy\n    environment:\n      PORT: 4000\n      POSTGRES_PORT: ${POSTGRES_PORT}\n      POSTGRES_HOST: ${POSTGRES_HOST}\n      POSTGRES_DB: ${POSTGRES_DB}\n      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}\n      DATABASE_URL: ecto://supabase_admin:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/_supabase\n      CLUSTER_POSTGRES: \"true\"\n      SECRET_KEY_BASE: ${SECRET_KEY_BASE}\n      VAULT_ENC_KEY: ${VAULT_ENC_KEY}\n      API_JWT_SECRET: ${JWT_SECRET}\n      METRICS_JWT_SECRET: ${JWT_SECRET}\n      REGION: local\n      ERL_AFLAGS: -proto_dist inet_tcp\n      POOLER_TENANT_ID: ${POOLER_TENANT_ID}\n      POOLER_DEFAULT_POOL_SIZE: ${POOLER_DEFAULT_POOL_SIZE}\n      POOLER_MAX_CLIENT_CONN: ${POOLER_MAX_CLIENT_CONN}\n      POOLER_POOL_MODE: transaction\n      DB_POOL_SIZE: ${POOLER_DB_POOL_SIZE}\n    command:\n      [\n        \"/bin/sh\",\n        \"-c\",\n        \"/app/bin/migrate && /app/bin/supavisor eval \\\"$$(cat /etc/pooler/pooler.exs)\\\" && /app/bin/server\"\n      ]\n\nvolumes:\n  db-config:\n  deno-cache:\n",
      "source_url": "https://github.com/supabase/supabase/blob/8ba2f0a63bc6d9cc00886557ffa027c5e8a83cfc/docker/docker-compose.yml",
      "validation_status": "config_ok"
    },
    {
      "order": 5,
      "kind": "shell",
      "language": "bash",
      "label": "Copy .env.example to .env",
      "body": "cp .env.example .env",
      "source_url": "https://github.com/supabase/supabase/blob/8ba2f0a63bc6d9cc00886557ffa027c5e8a83cfc/apps/docs/content/guides/self-hosting/docker.mdx",
      "validation_status": "unvalidated"
    },
    {
      "order": 6,
      "kind": "shell",
      "language": "bash",
      "label": "Generate secrets with the project script",
      "body": "sh utils/generate-keys.sh",
      "source_url": "https://github.com/supabase/supabase/blob/8ba2f0a63bc6d9cc00886557ffa027c5e8a83cfc/apps/docs/content/guides/self-hosting/docker.mdx",
      "validation_status": "unvalidated"
    },
    {
      "order": 7,
      "kind": "shell",
      "language": "bash",
      "label": "Add the API keys and the auth key pair",
      "body": "sh utils/add-new-auth-keys.sh",
      "source_url": "https://github.com/supabase/supabase/blob/8ba2f0a63bc6d9cc00886557ffa027c5e8a83cfc/apps/docs/content/guides/self-hosting/docker.mdx",
      "validation_status": "unvalidated"
    },
    {
      "order": 8,
      "kind": "note",
      "language": "text",
      "label": "What the compose file check ran with",
      "body": "The compose file check ran in a checkout of supabase/supabase at 8ba2f0a63bc6d9cc00886557ffa027c5e8a83cfc (a sparse checkout: the top-level files and the folder docker), in the folder docker, after .env.example was copied to .env. The self-hosting guide tells the reader to clone the repository, copy the docker folder, copy .env.example to .env and run docker compose. The check did not run these steps: sh utils/generate-keys.sh; sh utils/add-new-auth-keys.sh.",
      "source_url": "https://github.com/supabase/supabase/blob/8ba2f0a63bc6d9cc00886557ffa027c5e8a83cfc/apps/docs/content/guides/self-hosting/docker.mdx",
      "validation_status": "unvalidated"
    },
    {
      "order": 9,
      "kind": "shell",
      "language": "bash",
      "label": "Start it",
      "body": "docker compose up -d",
      "source_url": "https://github.com/supabase/supabase/blob/8ba2f0a63bc6d9cc00886557ffa027c5e8a83cfc/apps/docs/content/guides/self-hosting/docker.mdx",
      "validation_status": "unvalidated"
    },
    {
      "order": 10,
      "kind": "verify",
      "language": "text",
      "label": "Open the app",
      "body": "Open http://localhost:8000. The compose file publishes host port 8000 for the api-gw service. The docs name port 8000 for the API gateway and say the Studio dashboard is protected with HTTP basic authentication.",
      "source_url": "https://github.com/supabase/supabase/blob/8ba2f0a63bc6d9cc00886557ffa027c5e8a83cfc/docker/docker-compose.yml",
      "validation_status": "unvalidated"
    }
  ],
  "notes": [
    {
      "kind": "gotcha",
      "title": "The compose file mounts host folders",
      "body": "Host paths mounted into containers: ./volumes/snippets at /app/snippets (studio), ./volumes/functions at /app/edge-functions (studio), ./volumes/api/envoy/envoy.yaml at /etc/envoy/envoy.yaml (api-gw), ./volumes/api/envoy/cds.yaml at /etc/envoy/cds.yaml (api-gw), ./volumes/api/envoy/lds.template.yaml at /etc/envoy/lds.template.yaml (api-gw), ./volumes/api/envoy/docker-entrypoint.sh at /docker-entrypoint.sh (api-gw), ./volumes/storage at /var/lib/storage (storage), ./volumes/storage at /var/lib/storage (imgproxy) and 10 more.",
      "source_url": "https://github.com/supabase/supabase/blob/8ba2f0a63bc6d9cc00886557ffa027c5e8a83cfc/docker/docker-compose.yml",
      "checked_at": "2026-10-22"
    },
    {
      "kind": "gotcha",
      "title": "Do not start with the example secrets",
      "body": "The docs say never to start self-hosted Supabase with the placeholder passwords and keys from .env.example. They say to generate secrets, set a dashboard password and check the URL settings before the first start.",
      "source_url": "https://github.com/supabase/supabase/blob/8ba2f0a63bc6d9cc00886557ffa027c5e8a83cfc/apps/docs/content/guides/self-hosting/docker.mdx",
      "checked_at": "2026-10-22"
    }
  ],
  "signals": {
    "captured_at": "2026-10-22",
    "stars": 113283,
    "forks": 16989,
    "open_issues": 1187,
    "last_push": "2026-10-22",
    "days_since_push": 0,
    "latest_release": "v1.26.08",
    "latest_release_date": "2026-08-07",
    "days_since_release": 76
  },
  "links": {
    "html": "https://openlogs.fyi/tools/supabase",
    "markdown": "https://openlogs.fyi/tools/supabase.md",
    "json": "https://openlogs.fyi/tools/supabase.json"
  },
  "updated_at": "2026-10-22"
}
