Update Supabase Docker configuration and template (#872)

* Update Supabase Docker configuration and template

- Updated Supabase Studio and GoTrue images to the latest versions.
- Modified healthcheck commands for the Studio service.
- Enhanced environment variable configuration in docker-compose.yml for better flexibility.
- Updated Logflare API key references and added new S3 protocol access keys in template.toml.
- Introduced custom entrypoint script for Kong to handle Lua expressions and environment variable substitution.
- Added new routes and plugins for enhanced authentication and authorization in Kong configuration.

* Refactor Supabase Docker Compose and Template Configuration

- Removed container name prefixes from services in docker-compose.yml for consistency.
- Increased healthcheck retries and start periods for several services to improve stability.
- Updated healthcheck timeout and retry configurations for the analytics service.
- Adjusted appname generation logic in template.toml to ensure proper naming for services.
- Cleaned up commented sections in template.toml for clarity.
This commit is contained in:
Abdullah Istiak
2026-07-08 03:51:01 +06:00
committed by GitHub
parent 292716be4b
commit f2f4d85d43
2 changed files with 440 additions and 252 deletions

View File

@@ -1,89 +1,107 @@
# Usage
# Start: docker compose up
# With helpers: docker compose -f docker-compose.yml -f ./dev/docker-compose.dev.yml up
# Start: docker compose up -d
# Stop: docker compose down
# Destroy: docker compose -f docker-compose.yml -f ./dev/docker-compose.dev.yml down -v --remove-orphans
# Reset everything: ./reset.sh
# Reset everything: ./reset.sh
#
# Dokploy: Kong is exposed on port 8000 for domain routing. Storage uses local file
# backend by default; edit the storage service env in this file to switch to S3/MinIO.
name: supabase
services:
studio:
container_name: ${CONTAINER_PREFIX}-studio
image: supabase/studio:2025.04.21-sha-173cc56
image: supabase/studio:2026.04.27-sha-5f60601
restart: unless-stopped
healthcheck:
test:
[
"CMD",
"node",
"-e",
"fetch('http://studio:3000/api/platform/profile').then((r) => {if (r.status !== 200) throw new Error(r.status)})"
"CMD-SHELL",
"node -e \"fetch('http://localhost:3000/api/platform/profile').then((r) => {if (r.status !== 200) throw new Error(r.status)})\""
]
timeout: 10s
interval: 5s
retries: 3
retries: 5
start_period: 30s
depends_on:
analytics:
condition: service_healthy
environment:
HOSTNAME: "0.0.0.0"
STUDIO_PG_META_URL: http://meta:8080
POSTGRES_PORT: ${POSTGRES_PORT}
POSTGRES_HOST: ${POSTGRES_HOST}
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
PG_META_CRYPTO_KEY: ${PG_META_CRYPTO_KEY}
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}
DEFAULT_ORGANIZATION_NAME: ${STUDIO_DEFAULT_ORGANIZATION}
DEFAULT_PROJECT_NAME: ${STUDIO_DEFAULT_PROJECT}
OPENAI_API_KEY: ${OPENAI_API_KEY:-}
SUPABASE_URL: ${SUPABASE_PUBLIC_URL}
SUPABASE_URL: http://kong:8000
SUPABASE_PUBLIC_URL: ${SUPABASE_PUBLIC_URL}
SUPABASE_ANON_KEY: ${ANON_KEY}
SUPABASE_SERVICE_KEY: ${SERVICE_ROLE_KEY}
AUTH_JWT_SECRET: ${JWT_SECRET}
LOGFLARE_API_KEY: ${LOGFLARE_API_KEY}
LOGFLARE_API_KEY: ${LOGFLARE_PUBLIC_ACCESS_TOKEN}
LOGFLARE_PUBLIC_ACCESS_TOKEN: ${LOGFLARE_PUBLIC_ACCESS_TOKEN}
LOGFLARE_PRIVATE_ACCESS_TOKEN: ${LOGFLARE_PRIVATE_ACCESS_TOKEN}
LOGFLARE_URL: http://analytics:4000
NEXT_PUBLIC_ENABLE_LOGS: true
# Comment to use Big Query backend for analytics
NEXT_PUBLIC_ENABLE_LOGS: "true"
NEXT_ANALYTICS_BACKEND_PROVIDER: postgres
# Uncomment to use Big Query backend for analytics
# NEXT_ANALYTICS_BACKEND_PROVIDER: bigquery
SNIPPETS_MANAGEMENT_FOLDER: /app/snippets
EDGE_FUNCTIONS_MANAGEMENT_FOLDER: /app/edge-functions
volumes:
- ../files/volumes/snippets:/app/snippets:Z
- ../files/volumes/functions:/app/edge-functions:Z
kong:
container_name: ${CONTAINER_PREFIX}-kong
image: kong:2.8.1
image: kong/kong:3.9.1
restart: unless-stopped
# ports:
# - ${KONG_HTTP_PORT}:8000/tcp
# - ${KONG_HTTPS_PORT}:8443/tcp
healthcheck:
test: ["CMD", "kong", "health"]
interval: 5s
timeout: 5s
retries: 5
start_period: 30s
depends_on:
studio:
condition: service_healthy
expose:
- 8000
- 8443
volumes:
# https://github.com/supabase/supabase/issues/12661
- ../files/volumes/api/kong.yml:/home/kong/temp.yml:ro,z
depends_on:
analytics:
condition: service_healthy
- ../files/volumes/api/kong-entrypoint.sh:/home/kong/kong-entrypoint.sh:ro,z
environment:
KONG_DATABASE: "off"
KONG_DECLARATIVE_CONFIG: /home/kong/kong.yml
# https://github.com/supabase/cli/issues/14
KONG_DECLARATIVE_CONFIG: /usr/local/kong/kong.yml
KONG_DNS_ORDER: LAST,A,CNAME
KONG_PLUGINS: request-transformer,cors,key-auth,acl,basic-auth
KONG_DNS_NOT_FOUND_TTL: 1
KONG_PLUGINS: request-transformer,cors,key-auth,acl,basic-auth,request-termination,ip-restriction,post-function
KONG_NGINX_PROXY_PROXY_BUFFER_SIZE: 160k
KONG_NGINX_PROXY_PROXY_BUFFERS: 64 160k
KONG_PROXY_ACCESS_LOG: /dev/stdout combined
SUPABASE_ANON_KEY: ${ANON_KEY}
SUPABASE_SERVICE_KEY: ${SERVICE_ROLE_KEY}
SUPABASE_PUBLISHABLE_KEY: ${SUPABASE_PUBLISHABLE_KEY:-}
SUPABASE_SECRET_KEY: ${SUPABASE_SECRET_KEY:-}
ANON_KEY_ASYMMETRIC: ${ANON_KEY_ASYMMETRIC:-}
SERVICE_ROLE_KEY_ASYMMETRIC: ${SERVICE_ROLE_KEY_ASYMMETRIC:-}
DASHBOARD_USERNAME: ${DASHBOARD_USERNAME}
DASHBOARD_PASSWORD: ${DASHBOARD_PASSWORD}
CONTAINER_PREFIX: ${CONTAINER_PREFIX}
# https://unix.stackexchange.com/a/294837
entrypoint: bash -c 'eval "echo \"$$(cat ~/temp.yml)\"" > ~/kong.yml && /docker-entrypoint.sh kong docker-start'
entrypoint: ["/bin/bash", "/home/kong/kong-entrypoint.sh"]
auth:
container_name: ${CONTAINER_PREFIX}-auth
image: supabase/gotrue:v2.171.0
image: supabase/gotrue:v2.186.0
restart: unless-stopped
healthcheck:
test:
@@ -100,12 +118,8 @@ services:
retries: 3
depends_on:
db:
# Disable this if you are using an external Postgres database
condition: service_healthy
analytics:
condition: service_healthy
environment:
# the next line seems required if you want to be able to send mails from the supabase GUI
GOTRUE_MAILER_EXTERNAL_HOSTS: kong,${SUPABASE_HOST}
GOTRUE_API_HOST: 0.0.0.0
GOTRUE_API_PORT: 9999
@@ -123,16 +137,12 @@ services:
GOTRUE_JWT_DEFAULT_GROUP_NAME: authenticated
GOTRUE_JWT_EXP: ${JWT_EXPIRY}
GOTRUE_JWT_SECRET: ${JWT_SECRET}
GOTRUE_JWT_KEYS: ${JWT_KEYS:-[]}
GOTRUE_EXTERNAL_EMAIL_ENABLED: ${ENABLE_EMAIL_SIGNUP}
GOTRUE_EXTERNAL_ANONYMOUS_USERS_ENABLED: ${ENABLE_ANONYMOUS_USERS}
GOTRUE_MAILER_AUTOCONFIRM: ${ENABLE_EMAIL_AUTOCONFIRM}
# Uncomment to bypass nonce check in ID Token flow. Commonly set to true when using Google Sign In on mobile.
# GOTRUE_EXTERNAL_SKIP_NONCE_CHECK: true
# GOTRUE_MAILER_SECURE_EMAIL_CHANGE_ENABLED: true
# GOTRUE_SMTP_MAX_FREQUENCY: 1s
GOTRUE_SMTP_ADMIN_EMAIL: ${SMTP_ADMIN_EMAIL}
GOTRUE_SMTP_HOST: ${SMTP_HOST}
GOTRUE_SMTP_PORT: ${SMTP_PORT}
@@ -146,39 +156,18 @@ services:
GOTRUE_EXTERNAL_PHONE_ENABLED: ${ENABLE_PHONE_SIGNUP}
GOTRUE_SMS_AUTOCONFIRM: ${ENABLE_PHONE_AUTOCONFIRM}
# Uncomment to enable custom access token hook. Please see: https://supabase.com/docs/guides/auth/auth-hooks for full list of hooks and additional details about custom_access_token_hook
# GOTRUE_HOOK_CUSTOM_ACCESS_TOKEN_ENABLED: "true"
# GOTRUE_HOOK_CUSTOM_ACCESS_TOKEN_URI: "pg-functions://postgres/public/custom_access_token_hook"
# GOTRUE_HOOK_CUSTOM_ACCESS_TOKEN_SECRETS: "<standard-base64-secret>"
# GOTRUE_HOOK_MFA_VERIFICATION_ATTEMPT_ENABLED: "true"
# GOTRUE_HOOK_MFA_VERIFICATION_ATTEMPT_URI: "pg-functions://postgres/public/mfa_verification_attempt"
# GOTRUE_HOOK_PASSWORD_VERIFICATION_ATTEMPT_ENABLED: "true"
# GOTRUE_HOOK_PASSWORD_VERIFICATION_ATTEMPT_URI: "pg-functions://postgres/public/password_verification_attempt"
# GOTRUE_HOOK_SEND_SMS_ENABLED: "false"
# GOTRUE_HOOK_SEND_SMS_URI: "pg-functions://postgres/public/custom_access_token_hook"
# GOTRUE_HOOK_SEND_SMS_SECRETS: "v1,whsec_VGhpcyBpcyBhbiBleGFtcGxlIG9mIGEgc2hvcnRlciBCYXNlNjQgc3RyaW5n"
# GOTRUE_HOOK_SEND_EMAIL_ENABLED: "false"
# GOTRUE_HOOK_SEND_EMAIL_URI: "http://host.docker.internal:54321/functions/v1/email_sender"
# GOTRUE_HOOK_SEND_EMAIL_SECRETS: "v1,whsec_VGhpcyBpcyBhbiBleGFtcGxlIG9mIGEgc2hvcnRlciBCYXNlNjQgc3RyaW5n"
rest:
container_name: ${CONTAINER_PREFIX}-rest
image: postgrest/postgrest:v12.2.11
image: postgrest/postgrest:v14.8
restart: unless-stopped
depends_on:
db:
# Disable this if you are using an external Postgres database
condition: service_healthy
analytics:
condition: service_healthy
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_JWT_SECRET: ${JWT_SECRET}
PGRST_DB_USE_LEGACY_GUCS: "false"
@@ -190,32 +179,21 @@ services:
]
realtime:
# This container name looks inconsistent but is correct because realtime constructs tenant id by parsing the subdomain
container_name: realtime-dev.${CONTAINER_PREFIX}-realtime
image: supabase/realtime:v2.34.47
image: supabase/realtime:v2.76.5
restart: unless-stopped
depends_on:
db:
# Disable this if you are using an external Postgres database
condition: service_healthy
analytics:
condition: service_healthy
healthcheck:
test:
[
"CMD",
"curl",
"-sSfL",
"--head",
"-o",
"/dev/null",
"-H",
"Authorization: Bearer ${ANON_KEY}",
"http://localhost:4000/api/tenants/realtime-dev/health"
"CMD-SHELL",
"curl -sSfL --head -o /dev/null -H \"Authorization: Bearer ${ANON_KEY}\" http://localhost:4000/api/tenants/realtime-dev/health"
]
timeout: 5s
interval: 5s
interval: 30s
retries: 3
start_period: 10s
environment:
PORT: 4000
DB_HOST: ${POSTGRES_HOST}
@@ -227,20 +205,25 @@ services:
DB_ENC_KEY: supabaserealtime
API_JWT_SECRET: ${JWT_SECRET}
SECRET_KEY_BASE: ${SECRET_KEY_BASE}
METRICS_JWT_SECRET: ${JWT_SECRET}
ERL_AFLAGS: -proto_dist inet_tcp
DNS_NODES: "''"
RLIMIT_NOFILE: "10000"
APP_NAME: realtime
SEED_SELF_HOST: true
RUN_JANITOR: true
SEED_SELF_HOST: "true"
RUN_JANITOR: "true"
DISABLE_HEALTHCHECK_LOGGING: "true"
# To use S3 backed storage: docker compose -f docker-compose.yml -f docker-compose.s3.yml up
storage:
container_name: ${CONTAINER_PREFIX}-storage
image: supabase/storage-api:v1.22.7
image: supabase/storage-api:v1.48.26
restart: unless-stopped
volumes:
- ../files/volumes/storage:/var/lib/storage:z
depends_on:
db:
condition: service_healthy
rest:
condition: service_started
imgproxy:
condition: service_started
healthcheck:
test:
[
@@ -254,33 +237,30 @@ services:
timeout: 5s
interval: 5s
retries: 3
depends_on:
db:
# Disable this if you are using an external Postgres database
condition: service_healthy
rest:
condition: service_started
imgproxy:
condition: service_started
start_period: 10s
volumes:
- ../files/volumes/storage:/var/lib/storage:z
environment:
ANON_KEY: ${ANON_KEY}
SERVICE_KEY: ${SERVICE_ROLE_KEY}
POSTGREST_URL: http://rest:3000
PGRST_JWT_SECRET: ${JWT_SECRET}
AUTH_JWT_SECRET: ${JWT_SECRET}
DATABASE_URL: postgres://supabase_storage_admin:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}
STORAGE_PUBLIC_URL: ${SUPABASE_PUBLIC_URL}
REQUEST_ALLOW_X_FORWARDED_PATH: "true"
FILE_SIZE_LIMIT: 52428800
STORAGE_BACKEND: file
GLOBAL_S3_BUCKET: ${GLOBAL_S3_BUCKET}
FILE_STORAGE_BACKEND_PATH: /var/lib/storage
TENANT_ID: stub
# TODO: https://github.com/supabase/storage-api/issues/55
REGION: stub
GLOBAL_S3_BUCKET: stub
TENANT_ID: ${STORAGE_TENANT_ID}
REGION: ${REGION}
ENABLE_IMAGE_TRANSFORMATION: "true"
IMGPROXY_URL: http://imgproxy:5001
S3_PROTOCOL_ACCESS_KEY_ID: ${S3_PROTOCOL_ACCESS_KEY_ID}
S3_PROTOCOL_ACCESS_KEY_SECRET: ${S3_PROTOCOL_ACCESS_KEY_SECRET}
imgproxy:
container_name: ${CONTAINER_PREFIX}-imgproxy
image: darthsim/imgproxy:v3.8.0
image: darthsim/imgproxy:v3.30.1
restart: unless-stopped
volumes:
- ../files/volumes/storage:/var/lib/storage:z
@@ -298,17 +278,14 @@ services:
IMGPROXY_BIND: ":5001"
IMGPROXY_LOCAL_FILESYSTEM_ROOT: /
IMGPROXY_USE_ETAG: "true"
IMGPROXY_ENABLE_WEBP_DETECTION: ${IMGPROXY_ENABLE_WEBP_DETECTION}
IMGPROXY_AUTO_WEBP: ${IMGPROXY_AUTO_WEBP}
IMGPROXY_MAX_SRC_RESOLUTION: 16.8
meta:
container_name: ${CONTAINER_PREFIX}-meta
image: supabase/postgres-meta:v0.88.9
image: supabase/postgres-meta:v0.96.3
restart: unless-stopped
depends_on:
db:
# Disable this if you are using an external Postgres database
condition: service_healthy
analytics:
condition: service_healthy
environment:
PG_META_PORT: 8080
@@ -317,23 +294,26 @@ services:
PG_META_DB_NAME: ${POSTGRES_DB}
PG_META_DB_USER: supabase_admin
PG_META_DB_PASSWORD: ${POSTGRES_PASSWORD}
CRYPTO_KEY: ${PG_META_CRYPTO_KEY}
functions:
container_name: ${CONTAINER_PREFIX}-edge-functions
image: supabase/edge-runtime:v1.67.4
image: supabase/edge-runtime:v1.71.2
restart: unless-stopped
volumes:
- ../files/volumes/functions:/home/deno/functions:Z
- deno-cache:/root/.cache/deno
depends_on:
analytics:
kong:
condition: service_healthy
environment:
JWT_SECRET: ${JWT_SECRET}
SUPABASE_URL: http://kong:8000
SUPABASE_PUBLIC_URL: ${SUPABASE_PUBLIC_URL}
SUPABASE_ANON_KEY: ${ANON_KEY}
SUPABASE_SERVICE_ROLE_KEY: ${SERVICE_ROLE_KEY}
SUPABASE_PUBLISHABLE_KEYS: "{\"default\":\"${SUPABASE_PUBLISHABLE_KEY:-}\"}"
SUPABASE_SECRET_KEYS: "{\"default\":\"${SUPABASE_SECRET_KEY:-}\"}"
SUPABASE_DB_URL: postgresql://postgres:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}
# TODO: Allow configuring VERIFY_JWT per function. This PR might help: https://github.com/supabase/cli/pull/786
VERIFY_JWT: "${FUNCTIONS_VERIFY_JWT}"
command:
[
@@ -343,32 +323,23 @@ services:
]
analytics:
container_name: ${CONTAINER_PREFIX}-analytics
image: supabase/logflare:1.12.0
image: supabase/logflare:1.36.1
restart: unless-stopped
# ports:
# - 4000:4000
expose:
- 4000
# Uncomment to use Big Query backend for analytics
# volumes:
# - type: bind
# source: ${PWD}/gcloud.json
# target: /opt/app/rel/logflare/bin/gcloud.json
# read_only: true
# First boot: Logflare seeds _analytics in Postgres; allow extra time before marking unhealthy.
healthcheck:
test:
[
"CMD",
"curl",
"http://localhost:4000/health"
"CMD-SHELL",
"curl -sSfL -o /dev/null http://localhost:4000/health"
]
timeout: 5s
timeout: 10s
interval: 5s
retries: 10
retries: 15
start_period: 90s
depends_on:
db:
# Disable this if you are using an external Postgres database
condition: service_healthy
environment:
LOGFLARE_NODE_HOST: 127.0.0.1
@@ -378,55 +349,42 @@ services:
DB_PORT: ${POSTGRES_PORT}
DB_PASSWORD: ${POSTGRES_PASSWORD}
DB_SCHEMA: _analytics
LOGFLARE_API_KEY: ${LOGFLARE_API_KEY}
LOGFLARE_SINGLE_TENANT: true
LOGFLARE_SUPABASE_MODE: true
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
# Comment variables to use Big Query backend for analytics
POSTGRES_BACKEND_URL: postgresql://supabase_admin:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/_supabase
POSTGRES_BACKEND_SCHEMA: _analytics
LOGFLARE_FEATURE_FLAG_OVERRIDE: multibackend=true
# Uncomment to use Big Query backend for analytics
# GOOGLE_PROJECT_ID: ${GOOGLE_PROJECT_ID}
# GOOGLE_PROJECT_NUMBER: ${GOOGLE_PROJECT_NUMBER}
# Comment out everything below this point if you are using an external Postgres database
db:
container_name: ${CONTAINER_PREFIX}-db
image: supabase/postgres:15.8.1.060
image: supabase/postgres:15.8.1.085
restart: unless-stopped
volumes:
- ../files/volumes/db/realtime.sql:/docker-entrypoint-initdb.d/migrations/99-realtime.sql:Z
# Must be superuser to create event trigger
- ../files/volumes/db/webhooks.sql:/docker-entrypoint-initdb.d/init-scripts/98-webhooks.sql:Z
# Must be superuser to alter reserved role
- ../files/volumes/db/roles.sql:/docker-entrypoint-initdb.d/init-scripts/99-roles.sql:Z
# Initialize the database settings with JWT_SECRET and JWT_EXP
- ../files/volumes/db/jwt.sql:/docker-entrypoint-initdb.d/init-scripts/99-jwt.sql:Z
# PGDATA directory is persisted between restarts
- ../files/volumes/db/data:/var/lib/postgresql/data:Z
# Changes required for internal supabase data such as _analytics
- ../files/volumes/db/_supabase.sql:/docker-entrypoint-initdb.d/migrations/97-_supabase.sql:Z
# Changes required for Analytics support
- ../files/volumes/db/logs.sql:/docker-entrypoint-initdb.d/migrations/99-logs.sql:Z
# Changes required for Pooler support
- ../files/volumes/db/pooler.sql:/docker-entrypoint-initdb.d/migrations/99-pooler.sql:Z
# Use named volume to persist pgsodium decryption key between restarts
- db-config:/etc/postgresql-custom
healthcheck:
test:
[
"CMD",
"pg_isready",
"-U",
"postgres",
"-h",
"localhost"
"CMD",
"pg_isready",
"-U",
"postgres",
"-h",
"localhost"
]
interval: 5s
timeout: 5s
retries: 10
retries: 15
start_period: 60s
depends_on:
vector:
condition: service_healthy
@@ -446,12 +404,11 @@ services:
"-c",
"config_file=/etc/postgresql/postgresql.conf",
"-c",
"log_min_messages=fatal" # prevents Realtime polling queries from appearing in logs
"log_min_messages=fatal"
]
vector:
container_name: ${CONTAINER_PREFIX}-vector
image: timberio/vector:0.28.1-alpine
image: timberio/vector:0.53.0-alpine
restart: unless-stopped
volumes:
- ../files/volumes/logs/vector.yml:/etc/vector/vector.yml:ro,z
@@ -468,9 +425,10 @@ services:
]
timeout: 5s
interval: 5s
retries: 3
retries: 5
start_period: 10s
environment:
LOGFLARE_API_KEY: ${LOGFLARE_API_KEY}
LOGFLARE_PUBLIC_ACCESS_TOKEN: ${LOGFLARE_PUBLIC_ACCESS_TOKEN}
command:
[
"--config",
@@ -479,14 +437,12 @@ services:
security_opt:
- "label=disable"
# Update the DATABASE_URL if you are using an external Postgres database
supavisor:
container_name: ${CONTAINER_PREFIX}-pooler
image: supabase/supavisor:2.5.1
image: supabase/supavisor:2.7.4
restart: unless-stopped
ports: # expose supavisor to the host to enable db pooler connection
- ${POSTGRES_PORT}:5432
- ${POOLER_PROXY_PORT_TRANSACTION}:6543
expose:
- "5432"
- "6543"
volumes:
- ../files/volumes/pooler/pooler.exs:/etc/pooler/pooler.exs:ro,z
healthcheck:
@@ -502,19 +458,19 @@ services:
]
interval: 10s
timeout: 5s
retries: 5
retries: 10
start_period: 30s
depends_on:
db:
condition: service_healthy
analytics:
condition: service_healthy
environment:
PORT: 4000
POSTGRES_PORT: ${POSTGRES_PORT}
POSTGRES_HOST: ${POSTGRES_HOST}
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
DATABASE_URL: ecto://supabase_admin:${POSTGRES_PASSWORD}@db:${POSTGRES_PORT}/_supabase
CLUSTER_POSTGRES: true
DATABASE_URL: ecto://supabase_admin:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/_supabase
CLUSTER_POSTGRES: "true"
SECRET_KEY_BASE: ${SECRET_KEY_BASE}
VAULT_ENC_KEY: ${VAULT_ENC_KEY}
API_JWT_SECRET: ${JWT_SECRET}
@@ -525,6 +481,7 @@ services:
POOLER_DEFAULT_POOL_SIZE: ${POOLER_DEFAULT_POOL_SIZE}
POOLER_MAX_CLIENT_CONN: ${POOLER_MAX_CLIENT_CONN}
POOLER_POOL_MODE: transaction
DB_POOL_SIZE: ${POOLER_DB_POOL_SIZE}
command:
[
"/bin/sh",
@@ -533,4 +490,5 @@ services:
]
volumes:
db-config:
db-config:
deno-cache:

View File

@@ -2,11 +2,15 @@
main_domain = "${domain}"
postgres_password = "${password:32}"
dashboard_password = "${password:32}"
logflare_api_key = "${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}"
secret_key_base = "${password:64}"
vault_enc_key = "${password:32}"
jwt_secret = "${password:32}"
container_name_prefix = "${APP_NAME}-supabase"
pooler_tenant_id = "${uuid}"
anon_key_payload = """{
"role": "anon",
"iss": "supabase",
@@ -34,12 +38,7 @@ env = [
'# - ADDITIONAL_REDIRECT_URLS, SITE_URL should point to application using supabase for authentication',
'# They are used for redirecting after login/signup and gotrue will check them before sending emails',
'# - POSTGRES_PORT, POOLER_PROXY_PORT_TRANSACTION should be changed if you are already running other instances of supabase',
'#',
'# Supabase uses container names in part of its configuration so it is important to keep them',
'# This template generates a random prefix for the container names to avoid conflicts',
'# If you change it you will need to update routes in the vector.yml file in advanced->mounts section',
'############',
'CONTAINER_PREFIX=${container_name_prefix}',
'',
'############',
'# Secrets',
@@ -59,6 +58,9 @@ env = [
'DASHBOARD_PASSWORD=${dashboard_password}',
'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}',
'',
'',
'############',
@@ -68,7 +70,7 @@ env = [
'POSTGRES_HOST=db',
'POSTGRES_DB=postgres',
'POSTGRES_PORT=5432',
'# default user is postgres',
'POSTGRES_USER=postgres',
'',
'',
'############',
@@ -77,7 +79,8 @@ env = [
'POOLER_PROXY_PORT_TRANSACTION=6543',
'POOLER_DEFAULT_POOL_SIZE=20',
'POOLER_MAX_CLIENT_CONN=100',
'POOLER_TENANT_ID=your-tenant-id',
'POOLER_TENANT_ID=${pooler_tenant_id}',
'POOLER_DB_POOL_SIZE=5',
'',
'',
'############',
@@ -94,6 +97,8 @@ env = [
'############',
'',
'PGRST_DB_SCHEMAS=public,storage,graphql_public',
'PGRST_DB_MAX_ROWS=1000',
'PGRST_DB_EXTRA_SEARCH_PATH=public',
'',
'',
'############',
@@ -102,10 +107,11 @@ env = [
'',
'## General',
'SITE_URL=http://localhost:3000',
'ADDITIONAL_REDIRECT_URLS=http://${main_domain}/*,http://localhost:3000/*',
'ADDITIONAL_REDIRECT_URLS=https://${main_domain}/*,http://localhost:3000/*',
'JWT_EXPIRY=3600',
'DISABLE_SIGNUP=false',
'API_EXTERNAL_URL=http://${main_domain}',
'API_EXTERNAL_URL=https://${main_domain}',
'JWT_KEYS=[]',
'',
'## Mailer Config',
'MAILER_URLPATHS_CONFIRMATION="/auth/v1/verify"',
@@ -136,12 +142,19 @@ env = [
'STUDIO_DEFAULT_ORGANIZATION=Default Organization',
'STUDIO_DEFAULT_PROJECT=Default Project',
'',
'STUDIO_PORT=3000',
'# replace if you intend to use Studio outside of localhost',
'SUPABASE_PUBLIC_URL=http://${main_domain}',
'SUPABASE_PUBLIC_URL=https://${main_domain}',
'',
'# Enable webp support',
'IMGPROXY_ENABLE_WEBP_DETECTION=true',
'############',
'# Storage - local file backend (default). Edit docker-compose.yml storage env for S3/MinIO.',
'############',
'',
'GLOBAL_S3_BUCKET=stub',
'REGION=stub',
'STORAGE_TENANT_ID=stub',
'S3_PROTOCOL_ACCESS_KEY_ID=${s3_protocol_access_key_id}',
'S3_PROTOCOL_ACCESS_KEY_SECRET=${s3_protocol_access_key_secret}',
'',
'IMGPROXY_AUTO_WEBP=true',
'',
'# Add your OpenAI API key to enable SQL Editor Assistant',
'OPENAI_API_KEY=',
@@ -156,14 +169,8 @@ env = [
'',
'############',
'# Logs - Configuration for Logflare',
'# Please refer to https://supabase.com/docs/reference/self-hosting-analytics/introduction',
'############',
'',
'LOGFLARE_LOGGER_BACKEND_API_KEY=your-super-secret-and-long-logflare-key',
'',
'# Change vector.toml sinks to reflect this change',
'LOGFLARE_API_KEY=${logflare_api_key}',
'',
'# Docker socket location - this value will differ depending on your OS',
'DOCKER_SOCKET_LOCATION=/var/run/docker.sock',
'',
@@ -171,6 +178,40 @@ env = [
'GOOGLE_PROJECT_ID=GOOGLE_PROJECT_ID',
'GOOGLE_PROJECT_NUMBER=GOOGLE_PROJECT_NUMBER']
[[config.mounts]]
filePath = "/volumes/api/kong-entrypoint.sh"
content = """#!/bin/bash
# Custom entrypoint for Kong that builds Lua expressions for request-transformer
# and performs environment variable substitution in the declarative config.
if [ -n "$SUPABASE_SECRET_KEY" ] && [ -n "$SUPABASE_PUBLISHABLE_KEY" ]; then
export LUA_AUTH_EXPR="\$((headers.authorization ~= nil and headers.authorization:sub(1, 10) ~= 'Bearer sb_' and headers.authorization) or (headers.apikey == '$SUPABASE_SECRET_KEY' and 'Bearer $SERVICE_ROLE_KEY_ASYMMETRIC') or (headers.apikey == '$SUPABASE_PUBLISHABLE_KEY' and 'Bearer $ANON_KEY_ASYMMETRIC') or headers.apikey)"
export LUA_RT_WS_EXPR="\$((query_params.apikey == '$SUPABASE_SECRET_KEY' and '$SERVICE_ROLE_KEY_ASYMMETRIC') or (query_params.apikey == '$SUPABASE_PUBLISHABLE_KEY' and '$ANON_KEY_ASYMMETRIC') or query_params.apikey)"
else
export LUA_AUTH_EXPR="\$((headers.authorization ~= nil and headers.authorization:sub(1, 10) ~= 'Bearer sb_' and headers.authorization) or headers.apikey)"
export LUA_RT_WS_EXPR="\$(query_params.apikey)"
fi
awk '{
result = ""
rest = $0
while (match(rest, /\$[A-Za-z_][A-Za-z_0-9]*/)) {
varname = substr(rest, RSTART + 1, RLENGTH - 1)
if (varname in ENVIRON) {
result = result substr(rest, 1, RSTART - 1) ENVIRON[varname]
} else {
result = result substr(rest, 1, RSTART + RLENGTH - 1)
}
rest = substr(rest, RSTART + RLENGTH)
}
print result rest
}' /home/kong/temp.yml > "$KONG_DECLARATIVE_CONFIG"
sed -i '/^[[:space:]]*- key:[[:space:]]*$/d' "$KONG_DECLARATIVE_CONFIG"
exec /entrypoint.sh kong docker-start
"""
[[config.mounts]]
filePath = "/volumes/api/kong.yml"
content = """_format_version: '2.1'
@@ -184,9 +225,11 @@ consumers:
- username: anon
keyauth_credentials:
- key: $SUPABASE_ANON_KEY
- key: $SUPABASE_PUBLISHABLE_KEY
- username: service_role
keyauth_credentials:
- key: $SUPABASE_SERVICE_KEY
- key: $SUPABASE_SECRET_KEY
###
### Access Control List
@@ -202,8 +245,8 @@ acls:
###
basicauth_credentials:
- consumer: DASHBOARD
username: $DASHBOARD_USERNAME
password: $DASHBOARD_PASSWORD
username: '$DASHBOARD_USERNAME'
password: '$DASHBOARD_PASSWORD'
###
### API Routes
@@ -237,6 +280,33 @@ services:
- /auth/v1/authorize
plugins:
- name: cors
- name: auth-v1-open-jwks
url: http://auth:9999/.well-known/jwks.json
routes:
- name: auth-v1-open-jwks
strip_path: true
paths:
- /auth/v1/.well-known/jwks.json
plugins:
- name: cors
- name: auth-v1-open-sso-acs
url: http://auth:9999/sso/saml/acs
routes:
- name: auth-v1-open-sso-acs
strip_path: true
paths:
- /sso/saml/acs
plugins:
- name: cors
- name: auth-v1-open-sso-metadata
url: http://auth:9999/sso/saml/metadata
routes:
- name: auth-v1-open-sso-metadata
strip_path: true
paths:
- /sso/saml/metadata
plugins:
- name: cors
## Secure Auth routes
- name: auth-v1
@@ -252,6 +322,14 @@ services:
- name: key-auth
config:
hide_credentials: false
- name: request-transformer
config:
add:
headers:
- "Authorization: $LUA_AUTH_EXPR"
replace:
headers:
- "Authorization: $LUA_AUTH_EXPR"
- name: acl
config:
hide_groups_header: true
@@ -272,7 +350,15 @@ services:
- name: cors
- name: key-auth
config:
hide_credentials: true
hide_credentials: false
- name: request-transformer
config:
add:
headers:
- "Authorization: $LUA_AUTH_EXPR"
replace:
headers:
- "Authorization: $LUA_AUTH_EXPR"
- name: acl
config:
hide_groups_header: true
@@ -298,7 +384,11 @@ services:
config:
add:
headers:
- Content-Profile:graphql_public
- "Content-Profile: graphql_public"
- "Authorization: $LUA_AUTH_EXPR"
replace:
headers:
- "Authorization: $LUA_AUTH_EXPR"
- name: acl
config:
hide_groups_header: true
@@ -309,7 +399,7 @@ services:
## Secure Realtime routes
- name: realtime-v1-ws
_comment: 'Realtime: /realtime/v1/* -> ws://realtime:4000/socket/*'
url: http://realtime-dev.${CONTAINER_PREFIX}-realtime:4000/socket
url: http://realtime:4000/socket
protocol: ws
routes:
- name: realtime-v1-ws
@@ -321,6 +411,14 @@ services:
- name: key-auth
config:
hide_credentials: false
- name: request-transformer
config:
add:
headers:
- "x-api-key:$LUA_RT_WS_EXPR"
replace:
querystring:
- "apikey:$LUA_RT_WS_EXPR"
- name: acl
config:
hide_groups_header: true
@@ -328,8 +426,8 @@ services:
- admin
- anon
- name: realtime-v1-rest
_comment: 'Realtime: /realtime/v1/* -> ws://realtime:4000/socket/*'
url: http://realtime-dev.${CONTAINER_PREFIX}-realtime:4000/api
_comment: 'Realtime: /realtime/v1/api/* -> http://realtime:4000/api/*'
url: http://realtime:4000/api
protocol: http
routes:
- name: realtime-v1-rest
@@ -341,13 +439,22 @@ services:
- name: key-auth
config:
hide_credentials: false
- name: request-transformer
config:
add:
headers:
- "Authorization: $LUA_AUTH_EXPR"
replace:
headers:
- "Authorization: $LUA_AUTH_EXPR"
- name: acl
config:
hide_groups_header: true
allow:
- admin
- anon
## Storage routes: the storage server manages its own auth
## Storage routes
- name: storage-v1
_comment: 'Storage: /storage/v1/* -> http://storage:5000/*'
url: http://storage:5000/
@@ -358,11 +465,28 @@ services:
- /storage/v1/
plugins:
- name: cors
- name: request-transformer
config:
add:
headers:
- "Authorization: $LUA_AUTH_EXPR"
replace:
headers:
- "Authorization: $LUA_AUTH_EXPR"
- name: post-function
config:
access:
- |
local auth = kong.request.get_header("authorization")
if auth == nil or auth == "" or auth:find("^%s*$") then
kong.service.request.clear_header("authorization")
end
## Edge Functions routes
- name: functions-v1
_comment: 'Edge Functions: /functions/v1/* -> http://functions:9000/*'
url: http://functions:9000/
read_timeout: 150000
routes:
- name: functions-v1-all
strip_path: true
@@ -371,15 +495,16 @@ services:
plugins:
- name: cors
## Analytics routes
- name: analytics-v1
_comment: 'Analytics: /analytics/v1/* -> http://logflare:4000/*'
url: http://analytics:4000/
## OAuth 2.0 Authorization Server Metadata
- name: well-known-oauth
url: http://auth:9999/.well-known/oauth-authorization-server
routes:
- name: analytics-v1-all
- name: well-known-oauth
strip_path: true
paths:
- /analytics/v1/
- /.well-known/oauth-authorization-server
plugins:
- name: cors
## Secure Database routes
- name: meta
@@ -400,6 +525,32 @@ services:
allow:
- admin
- name: mcp-blocker
url: http://studio:3000/api/mcp
routes:
- name: mcp-blocker-route
strip_path: true
paths:
- /api/mcp
plugins:
- name: request-termination
config:
status_code: 403
message: "Access is forbidden."
- name: mcp
url: http://studio:3000/api/mcp
routes:
- name: mcp
strip_path: true
paths:
- /mcp
plugins:
- name: request-termination
config:
status_code: 403
message: "Access is forbidden."
## Protected Dashboard - catch all remaining routes
- name: dashboard
_comment: 'Studio: /* -> http://studio:3000/*'
@@ -416,6 +567,10 @@ services:
hide_credentials: true
"""
[[config.mounts]]
filePath = "/volumes/snippets/.gitkeep"
content = ""
[[config.mounts]]
filePath = "/volumes/db/init/data.sql"
content = ""
@@ -717,14 +872,25 @@ serve(async () => {
[[config.mounts]]
filePath = "/volumes/functions/main/index.ts"
content = """import { serve } from 'https://deno.land/std@0.131.0/http/server.ts'
import * as jose from 'https://deno.land/x/jose@v4.14.4/index.ts'
content = """import * as jose from 'https://deno.land/x/jose@v4.14.4/index.ts'
console.log('main function started')
const JWT_SECRET = Deno.env.get('JWT_SECRET')
const SUPABASE_URL = Deno.env.get('SUPABASE_URL')
const VERIFY_JWT = Deno.env.get('VERIFY_JWT') === 'true'
let SUPABASE_JWT_KEYS: ReturnType<typeof jose.createRemoteJWKSet> | null = null
if (SUPABASE_URL) {
try {
SUPABASE_JWT_KEYS = jose.createRemoteJWKSet(
new URL('/auth/v1/.well-known/jwks.json', SUPABASE_URL)
)
} catch (e) {
console.error('Failed to fetch JWKS from SUPABASE_URL:', e)
}
}
function getAuthToken(req: Request) {
const authHeader = req.headers.get('authorization')
if (!authHeader) {
@@ -737,25 +903,53 @@ function getAuthToken(req: Request) {
return token
}
async function verifyJWT(jwt: string): Promise<boolean> {
async function isValidLegacyJWT(jwt: string): Promise<boolean> {
if (!JWT_SECRET) {
console.error('JWT_SECRET not available for HS256 token verification')
return false
}
const encoder = new TextEncoder()
const secretKey = encoder.encode(JWT_SECRET)
try {
await jose.jwtVerify(jwt, secretKey)
} catch (err) {
console.error(err)
} catch (e) {
console.error('Symmetric Legacy JWT verification error', e)
return false
}
return true
}
serve(async (req: Request) => {
async function isValidJWT(jwt: string): Promise<boolean> {
if (!SUPABASE_JWT_KEYS) {
console.error('JWKS not available for ES256/RS256 token verification')
return false
}
try {
await jose.jwtVerify(jwt, SUPABASE_JWT_KEYS)
} catch (e) {
console.error('Asymmetric JWT verification error', e)
return false
}
return true
}
async function isValidHybridJWT(jwt: string): Promise<boolean> {
const { alg: jwtAlgorithm } = jose.decodeProtectedHeader(jwt)
if (jwtAlgorithm === 'HS256') {
return await isValidLegacyJWT(jwt)
}
if (jwtAlgorithm === 'ES256' || jwtAlgorithm === 'RS256') {
return await isValidJWT(jwt)
}
return false
}
Deno.serve(async (req: Request) => {
if (req.method !== 'OPTIONS' && VERIFY_JWT) {
try {
const token = getAuthToken(req)
const isValidJWT = await verifyJWT(token)
if (!isValidJWT) {
const isValid = await isValidHybridJWT(token)
if (!isValid) {
return new Response(JSON.stringify({ msg: 'Invalid JWT' }), {
status: 401,
headers: { 'Content-Type': 'application/json' },
@@ -822,8 +1016,6 @@ content = """api:
sources:
docker_host:
type: docker_logs
exclude_containers:
- ${container_name_prefix}-vector
transforms:
project_logs:
@@ -833,7 +1025,15 @@ transforms:
source: |-
.project = "default"
.event_message = del(.message)
.appname = replace!(del(.container_name), "${container_name_prefix}", "supabase")
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)
@@ -847,12 +1047,12 @@ transforms:
inputs:
- project_logs
route:
kong: '.appname == "supabase-kong"'
kong: '.appname == "supabase-kong" || .appname == "supabase-envoy"'
auth: '.appname == "supabase-auth"'
rest: '.appname == "supabase-rest"'
realtime: '.appname == "realtime-dev.${CONTAINER_PREFIX}-realtime"'
realtime: '.appname == "supabase-realtime"'
storage: '.appname == "supabase-storage"'
functions: '.appname == "supabase-edge-functions"'
functions: '.appname == "supabase-functions"'
db: '.appname == "supabase-db"'
# Ignores non nginx errors since they are related with kong booting up
kong_logs:
@@ -866,15 +1066,17 @@ transforms:
.metadata.request.headers.referer = req.referer
.metadata.request.headers.user_agent = req.agent
.metadata.request.headers.cf_connecting_ip = req.client
.metadata.request.method = req.method
.metadata.request.path = req.path
.metadata.request.protocol = req.protocol
.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
}
# Ignores non nginx errors since they are related with kong booting up
kong_err:
type: remap
inputs:
@@ -918,14 +1120,18 @@ transforms:
parsed, err = parse_regex(.event_message, r'^(?P<time>.*): (?P<msg>.*)$')
if err == null {
.event_message = parsed.msg
.timestamp = to_timestamp!(parsed.time)
.timestamp = parse_timestamp!(value: parsed.time,format: "%d/%b/%Y:%H:%M:%S %z")
.metadata.host = .project
}
# Realtime logs are structured so we parse the severity level using regex (ignore time because it has no date)
realtime_logs_filtered:
type: filter
inputs:
- router.realtime
condition: '!contains(string!(.event_message), "/health")'
realtime_logs:
type: remap
inputs:
- router.realtime
- realtime_logs_filtered
source: |-
.metadata.project = del(.project)
.metadata.external_id = .metadata.project
@@ -934,6 +1140,12 @@ transforms:
.event_message = parsed.msg
.metadata.level = parsed.level
}
functions_logs:
type: remap
inputs:
- router.functions
source: |-
.metadata.project_ref = del(.project)
# Storage logs may contain json objects so we parse them for completeness
storage_logs:
type: remap
@@ -964,7 +1176,7 @@ transforms:
if err != null || parsed == null {
.metadata.parsed.error_severity = "info"
}
if parsed != null {
if parsed.level != null {
.metadata.parsed.error_severity = parsed.level
}
if .metadata.parsed.error_severity == "info" {
@@ -981,8 +1193,11 @@ sinks:
codec: 'json'
method: 'post'
request:
retry_max_duration_secs: 10
uri: 'http://analytics:4000/api/logs?source_name=gotrue.logs.prod&api_key=${LOGFLARE_API_KEY?LOGFLARE_API_KEY is required}'
retry_max_duration_secs: 30
retry_initial_backoff_secs: 1
headers:
x-api-key: ${LOGFLARE_PUBLIC_ACCESS_TOKEN?LOGFLARE_PUBLIC_ACCESS_TOKEN is required}
uri: 'http://analytics:4000/api/logs?source_name=gotrue.logs.prod'
logflare_realtime:
type: 'http'
inputs:
@@ -991,8 +1206,11 @@ sinks:
codec: 'json'
method: 'post'
request:
retry_max_duration_secs: 10
uri: 'http://analytics:4000/api/logs?source_name=realtime.logs.prod&api_key=${LOGFLARE_API_KEY?LOGFLARE_API_KEY is required}'
retry_max_duration_secs: 30
retry_initial_backoff_secs: 1
headers:
x-api-key: ${LOGFLARE_PUBLIC_ACCESS_TOKEN?LOGFLARE_PUBLIC_ACCESS_TOKEN is required}
uri: 'http://analytics:4000/api/logs?source_name=realtime.logs.prod'
logflare_rest:
type: 'http'
inputs:
@@ -1001,8 +1219,11 @@ sinks:
codec: 'json'
method: 'post'
request:
retry_max_duration_secs: 10
uri: 'http://analytics:4000/api/logs?source_name=postgREST.logs.prod&api_key=${LOGFLARE_API_KEY?LOGFLARE_API_KEY is required}'
retry_max_duration_secs: 30
retry_initial_backoff_secs: 1
headers:
x-api-key: ${LOGFLARE_PUBLIC_ACCESS_TOKEN?LOGFLARE_PUBLIC_ACCESS_TOKEN is required}
uri: 'http://analytics:4000/api/logs?source_name=postgREST.logs.prod'
logflare_db:
type: 'http'
inputs:
@@ -1011,21 +1232,24 @@ sinks:
codec: 'json'
method: 'post'
request:
retry_max_duration_secs: 10
# We must route the sink through kong because ingesting logs before logflare is fully initialised will
# lead to broken queries from studio. This works by the assumption that containers are started in the
# following order: vector > db > logflare > kong
uri: 'http://kong:8000/analytics/v1/api/logs?source_name=postgres.logs&api_key=${LOGFLARE_API_KEY?LOGFLARE_API_KEY is required}'
retry_max_duration_secs: 30
retry_initial_backoff_secs: 1
headers:
x-api-key: ${LOGFLARE_PUBLIC_ACCESS_TOKEN?LOGFLARE_PUBLIC_ACCESS_TOKEN is required}
uri: 'http://analytics:4000/api/logs?source_name=postgres.logs'
logflare_functions:
type: 'http'
inputs:
- router.functions
- functions_logs
encoding:
codec: 'json'
method: 'post'
request:
retry_max_duration_secs: 10
uri: 'http://analytics:4000/api/logs?source_name=deno-relay-logs&api_key=${LOGFLARE_API_KEY?LOGFLARE_API_KEY is required}'
retry_max_duration_secs: 30
retry_initial_backoff_secs: 1
headers:
x-api-key: ${LOGFLARE_PUBLIC_ACCESS_TOKEN?LOGFLARE_PUBLIC_ACCESS_TOKEN is required}
uri: 'http://analytics:4000/api/logs?source_name=deno-relay-logs'
logflare_storage:
type: 'http'
inputs:
@@ -1034,8 +1258,11 @@ sinks:
codec: 'json'
method: 'post'
request:
retry_max_duration_secs: 10
uri: 'http://analytics:4000/api/logs?source_name=storage.logs.prod.2&api_key=${LOGFLARE_API_KEY?LOGFLARE_API_KEY is required}'
retry_max_duration_secs: 30
retry_initial_backoff_secs: 1
headers:
x-api-key: ${LOGFLARE_PUBLIC_ACCESS_TOKEN?LOGFLARE_PUBLIC_ACCESS_TOKEN is required}
uri: 'http://analytics:4000/api/logs?source_name=storage.logs.prod.2'
logflare_kong:
type: 'http'
inputs:
@@ -1045,8 +1272,11 @@ sinks:
codec: 'json'
method: 'post'
request:
retry_max_duration_secs: 10
uri: 'http://analytics:4000/api/logs?source_name=cloudflare.logs.prod&api_key=${LOGFLARE_API_KEY?LOGFLARE_API_KEY is required}'
retry_max_duration_secs: 30
retry_initial_backoff_secs: 1
headers:
x-api-key: ${LOGFLARE_PUBLIC_ACCESS_TOKEN?LOGFLARE_PUBLIC_ACCESS_TOKEN is required}
uri: 'http://analytics:4000/api/logs?source_name=cloudflare.logs.prod'
"""
[[config.mounts]]
@@ -1061,7 +1291,7 @@ content = """{:ok, _} = Application.ensure_all_started(:supavisor)
params = %{
"external_id" => System.get_env("POOLER_TENANT_ID"),
"db_host" => "db",
"db_host" => System.get_env("POSTGRES_HOST") || "db",
"db_port" => System.get_env("POSTGRES_PORT"),
"db_database" => System.get_env("POSTGRES_DB"),
"require_user" => false,