From 17a53fa567c9ac04f588fbd73a4978c88ca03b5c Mon Sep 17 00:00:00 2001 From: Mauricio Siu Date: Thu, 6 Aug 2026 01:09:28 -0600 Subject: [PATCH] fix(supabase): upgrade database to Postgres 17 Align the template with the current upstream self-hosted stack (supabase/postgres 17.6.1.136, studio 2026.08.03, kong 3.9.3), remove the deprecated analytics/vector services and switch Studio/pg-meta to the postgres role per upstream guidance. Closes #1067 Co-Authored-By: Claude Fable 5 --- blueprints/supabase/docker-compose.yml | 102 ++------- blueprints/supabase/meta.json | 2 +- blueprints/supabase/template.toml | 290 +------------------------ 3 files changed, 23 insertions(+), 371 deletions(-) diff --git a/blueprints/supabase/docker-compose.yml b/blueprints/supabase/docker-compose.yml index f6fed3b50..8a193e6ca 100644 --- a/blueprints/supabase/docker-compose.yml +++ b/blueprints/supabase/docker-compose.yml @@ -11,7 +11,7 @@ name: supabase services: studio: - image: supabase/studio:2026.06.03-sha-0bca601 + image: supabase/studio:2026.08.03-sha-022b374 restart: unless-stopped healthcheck: test: @@ -23,9 +23,6 @@ services: interval: 5s retries: 5 start_period: 30s - depends_on: - analytics: - condition: service_healthy environment: HOSTNAME: "0.0.0.0" @@ -35,6 +32,9 @@ services: POSTGRES_DB: ${POSTGRES_DB} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + # See: https://supabase.com/docs/guides/self-hosting/remove-superuser-access + POSTGRES_USER_READ_WRITE: postgres + PG_META_CRYPTO_KEY: ${PG_META_CRYPTO_KEY} PGRST_DB_SCHEMAS: ${PGRST_DB_SCHEMAS} PGRST_DB_MAX_ROWS: ${PGRST_DB_MAX_ROWS:-1000} @@ -49,14 +49,12 @@ services: SUPABASE_ANON_KEY: ${ANON_KEY} SUPABASE_SERVICE_KEY: ${SERVICE_ROLE_KEY} AUTH_JWT_SECRET: ${JWT_SECRET} + SUPABASE_PUBLISHABLE_KEY: ${SUPABASE_PUBLISHABLE_KEY:-} + SUPABASE_SECRET_KEY: ${SUPABASE_SECRET_KEY:-} - LOGFLARE_API_KEY: ${LOGFLARE_PUBLIC_ACCESS_TOKEN} - LOGFLARE_PUBLIC_ACCESS_TOKEN: ${LOGFLARE_PUBLIC_ACCESS_TOKEN} - LOGFLARE_PRIVATE_ACCESS_TOKEN: ${LOGFLARE_PRIVATE_ACCESS_TOKEN} + # Analytics (Logflare) was removed from the upstream self-hosted stack + ENABLED_FEATURES_LOGS_ALL: "false" - LOGFLARE_URL: http://analytics:4000 - NEXT_PUBLIC_ENABLE_LOGS: "true" - NEXT_ANALYTICS_BACKEND_PROVIDER: postgres SNIPPETS_MANAGEMENT_FOLDER: /app/snippets EDGE_FUNCTIONS_MANAGEMENT_FOLDER: /app/edge-functions volumes: @@ -64,7 +62,7 @@ services: - ../files/volumes/functions:/app/edge-functions:Z kong: - image: kong/kong:3.9.1 + image: kong/kong:3.9.3 restart: unless-stopped healthcheck: test: ["CMD", "kong", "health"] @@ -138,6 +136,7 @@ services: GOTRUE_JWT_EXP: ${JWT_EXPIRY} GOTRUE_JWT_SECRET: ${JWT_SECRET} GOTRUE_JWT_KEYS: ${JWT_KEYS:-[]} + GOTRUE_JWT_ISSUER: ${API_EXTERNAL_URL} GOTRUE_EXTERNAL_EMAIL_ENABLED: ${ENABLE_EMAIL_SIGNUP} GOTRUE_EXTERNAL_ANONYMOUS_USERS_ENABLED: ${ENABLE_ANONYMOUS_USERS} @@ -163,12 +162,19 @@ services: depends_on: db: condition: service_healthy + healthcheck: + test: [ "CMD", "postgrest", "--ready" ] + interval: 5s + timeout: 5s + retries: 3 environment: PGRST_DB_URI: postgres://authenticator:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB} PGRST_DB_SCHEMAS: ${PGRST_DB_SCHEMAS} PGRST_DB_MAX_ROWS: ${PGRST_DB_MAX_ROWS:-1000} PGRST_DB_EXTRA_SEARCH_PATH: ${PGRST_DB_EXTRA_SEARCH_PATH:-public} PGRST_DB_ANON_ROLE: anon + PGRST_ADMIN_SERVER_PORT: 3001 + PGRST_ADMIN_SERVER_HOST: localhost PGRST_JWT_SECRET: ${JWT_SECRET} PGRST_DB_USE_LEGACY_GUCS: "false" PGRST_APP_SETTINGS_JWT_SECRET: ${JWT_SECRET} @@ -292,7 +298,8 @@ services: PG_META_DB_HOST: ${POSTGRES_HOST} PG_META_DB_PORT: ${POSTGRES_PORT} PG_META_DB_NAME: ${POSTGRES_DB} - PG_META_DB_USER: supabase_admin + # See: https://supabase.com/docs/guides/self-hosting/remove-superuser-access + PG_META_DB_USER: postgres PG_META_DB_PASSWORD: ${POSTGRES_PASSWORD} CRYPTO_KEY: ${PG_META_CRYPTO_KEY} @@ -322,43 +329,9 @@ services: "/home/deno/functions/main" ] - analytics: - image: supabase/logflare:1.36.1 - restart: unless-stopped - expose: - - 4000 - # First boot: Logflare seeds _analytics in Postgres; allow extra time before marking unhealthy. - healthcheck: - test: - [ - "CMD-SHELL", - "curl -sSfL -o /dev/null http://localhost:4000/health" - ] - timeout: 10s - interval: 5s - retries: 15 - start_period: 90s - depends_on: - db: - condition: service_healthy - environment: - LOGFLARE_NODE_HOST: 127.0.0.1 - DB_USERNAME: supabase_admin - DB_DATABASE: _supabase - DB_HOSTNAME: ${POSTGRES_HOST} - DB_PORT: ${POSTGRES_PORT} - DB_PASSWORD: ${POSTGRES_PASSWORD} - DB_SCHEMA: _analytics - LOGFLARE_PUBLIC_ACCESS_TOKEN: ${LOGFLARE_PUBLIC_ACCESS_TOKEN} - LOGFLARE_PRIVATE_ACCESS_TOKEN: ${LOGFLARE_PRIVATE_ACCESS_TOKEN} - LOGFLARE_SINGLE_TENANT: "true" - LOGFLARE_SUPABASE_MODE: "true" - LOGFLARE_MIN_CLUSTER_SIZE: 1 - POSTGRES_BACKEND_URL: postgresql://supabase_admin:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/_supabase - POSTGRES_BACKEND_SCHEMA: _analytics - LOGFLARE_FEATURE_FLAG_OVERRIDE: multibackend=true - db: + # Major upgrade from Postgres 15 requires a dump/restore for existing installs. + # See: https://github.com/orgs/supabase/discussions/46080 image: supabase/postgres:17.6.1.136 restart: unless-stopped volumes: @@ -385,9 +358,6 @@ services: timeout: 5s retries: 15 start_period: 60s - depends_on: - vector: - condition: service_healthy environment: POSTGRES_HOST: /var/run/postgresql PGPORT: ${POSTGRES_PORT} @@ -407,36 +377,6 @@ services: "log_min_messages=fatal" ] - vector: - image: timberio/vector:0.53.0-alpine - restart: unless-stopped - volumes: - - ../files/volumes/logs/vector.yml:/etc/vector/vector.yml:ro,z - - ${DOCKER_SOCKET_LOCATION}:/var/run/docker.sock:ro,z - healthcheck: - test: - [ - "CMD", - "wget", - "--no-verbose", - "--tries=1", - "--spider", - "http://vector:9001/health" - ] - timeout: 5s - interval: 5s - retries: 5 - start_period: 10s - environment: - LOGFLARE_PUBLIC_ACCESS_TOKEN: ${LOGFLARE_PUBLIC_ACCESS_TOKEN} - command: - [ - "--config", - "/etc/vector/vector.yml" - ] - security_opt: - - "label=disable" - supavisor: image: supabase/supavisor:2.9.5 restart: unless-stopped diff --git a/blueprints/supabase/meta.json b/blueprints/supabase/meta.json index d5d2cdadf..d604c052e 100644 --- a/blueprints/supabase/meta.json +++ b/blueprints/supabase/meta.json @@ -1,7 +1,7 @@ { "id": "supabase", "name": "SupaBase", - "version": "2026.06.03 / dokploy >= 0.22.5", + "version": "2026.08.03 / dokploy >= 0.22.5", "description": "The open source Firebase alternative. Supabase gives you a dedicated Postgres database to build your web, mobile, and AI applications. This require at least version 0.22.5 of dokploy.", "links": { "github": "https://github.com/supabase/supabase", diff --git a/blueprints/supabase/template.toml b/blueprints/supabase/template.toml index 0e76b7dd4..bf64558a6 100644 --- a/blueprints/supabase/template.toml +++ b/blueprints/supabase/template.toml @@ -2,8 +2,6 @@ main_domain = "${domain}" postgres_password = "${password:32}" dashboard_password = "${password:32}" -logflare_public_access_token = "${password:32}" -logflare_private_access_token = "${password:32}" pg_meta_crypto_key = "${password:32}" s3_protocol_access_key_id = "${password:24}" s3_protocol_access_key_secret = "${password:48}" @@ -59,8 +57,6 @@ env = [ 'SECRET_KEY_BASE=${secret_key_base}', 'VAULT_ENC_KEY=${vault_enc_key}', 'PG_META_CRYPTO_KEY=${pg_meta_crypto_key}', -'LOGFLARE_PUBLIC_ACCESS_TOKEN=${logflare_public_access_token}', -'LOGFLARE_PRIVATE_ACCESS_TOKEN=${logflare_private_access_token}', '', '', '############', @@ -164,19 +160,7 @@ env = [ '# Functions - Configuration for Functions', '############', '# NOTE: VERIFY_JWT applies to all functions. Per-function VERIFY_JWT is not supported yet.', -'FUNCTIONS_VERIFY_JWT=false', -'', -'', -'############', -'# Logs - Configuration for Logflare', -'############', -'', -'# Docker socket location - this value will differ depending on your OS', -'DOCKER_SOCKET_LOCATION=/var/run/docker.sock', -'', -'# Google Cloud Project details', -'GOOGLE_PROJECT_ID=GOOGLE_PROJECT_ID', -'GOOGLE_PROJECT_NUMBER=GOOGLE_PROJECT_NUMBER'] +'FUNCTIONS_VERIFY_JWT=false'] [[config.mounts]] filePath = "/volumes/api/kong-entrypoint.sh" @@ -1007,278 +991,6 @@ Deno.serve(async (req: Request) => { }) """ -[[config.mounts]] -filePath = "/volumes/logs/vector.yml" -content = """api: - enabled: true - address: 0.0.0.0:9001 - -sources: - docker_host: - type: docker_logs - -transforms: - project_logs: - type: remap - inputs: - - docker_host - source: |- - .project = "default" - .event_message = del(.message) - compose_service, label_err = get(.label, ["com.docker.compose.service"]) - if label_err != null || compose_service == null { - abort - } - compose_service = to_string!(compose_service) - if compose_service == "vector" { - abort - } - .appname = "supabase-" + compose_service - del(.container_created_at) - del(.container_id) - del(.source_type) - del(.stream) - del(.label) - del(.image) - del(.host) - del(.stream) - router: - type: route - inputs: - - project_logs - route: - kong: '.appname == "supabase-kong" || .appname == "supabase-envoy"' - auth: '.appname == "supabase-auth"' - rest: '.appname == "supabase-rest"' - realtime: '.appname == "supabase-realtime"' - storage: '.appname == "supabase-storage"' - functions: '.appname == "supabase-functions"' - db: '.appname == "supabase-db"' - # Ignores non nginx errors since they are related with kong booting up - kong_logs: - type: remap - inputs: - - router.kong - source: |- - req, err = parse_nginx_log(.event_message, "combined") - if err == null { - .timestamp = req.timestamp - .metadata.request.headers.referer = req.referer - .metadata.request.headers.user_agent = req.agent - .metadata.request.headers.cf_connecting_ip = req.client - .metadata.response.status_code = req.status - url, split_err = split(req.request, " ") - if split_err == null { - .metadata.request.method = url[0] - .metadata.request.path = url[1] - .metadata.request.protocol = url[2] - } - } - if err != null { - abort - } - kong_err: - type: remap - inputs: - - router.kong - source: |- - .metadata.request.method = "GET" - .metadata.response.status_code = 200 - parsed, err = parse_nginx_log(.event_message, "error") - if err == null { - .timestamp = parsed.timestamp - .severity = parsed.severity - .metadata.request.host = parsed.host - .metadata.request.headers.cf_connecting_ip = parsed.client - url, err = split(parsed.request, " ") - if err == null { - .metadata.request.method = url[0] - .metadata.request.path = url[1] - .metadata.request.protocol = url[2] - } - } - if err != null { - abort - } - # Gotrue logs are structured json strings which frontend parses directly. But we keep metadata for consistency. - auth_logs: - type: remap - inputs: - - router.auth - source: |- - parsed, err = parse_json(.event_message) - if err == null { - .metadata.timestamp = parsed.time - .metadata = merge!(.metadata, parsed) - } - # PostgREST logs are structured so we separate timestamp from message using regex - rest_logs: - type: remap - inputs: - - router.rest - source: |- - parsed, err = parse_regex(.event_message, r'^(?P