Supabase Self-Hosted
Self-hosted Supabase with PostgreSQL, GoTrue auth, PostgREST, and Realtime.
Overview
Supabase is an open-source Firebase alternative that combines PostgreSQL with a comprehensive backend-as-a-service platform, providing real-time subscriptions, authentication, instant APIs, and file storage. This self-hosted stack integrates PostgreSQL as the core database with PostgREST for automatic API generation, GoTrue for authentication management, Realtime for WebSocket connections, and Kong Gateway for API routing and security. The platform transforms a traditional PostgreSQL database into a modern, API-first backend with real-time capabilities, eliminating the need to build authentication systems, API endpoints, and WebSocket infrastructure from scratch. This configuration is ideal for developers who want the full power of Supabase without vendor lock-in, organizations requiring data sovereignty, and teams building applications that need PostgreSQL's advanced features alongside modern backend services. The stack provides enterprise-grade authentication flows, automatic REST and GraphQL APIs from database schemas, real-time database change subscriptions, and secure file storage with access controls.
Key Features
- Automatic REST API generation from PostgreSQL schemas using PostgREST with row-level security
- Real-time database change subscriptions via WebSockets for live application updates
- Complete authentication system with GoTrue supporting email, OAuth, and magic link flows
- File storage API with access control policies and automatic image transformations
- Kong Gateway providing rate limiting, CORS handling, and API authentication middleware
- PostgreSQL Row Level Security (RLS) integration for fine-grained data access control
- Supabase Studio web interface for database management and API testing
- JWT-based authentication with configurable token expiration and refresh flows
Common Use Cases
- 1Building real-time collaborative applications like chat systems or document editors
- 2Creating SaaS platforms requiring multi-tenant data isolation with PostgreSQL RLS
- 3Developing mobile applications needing offline sync and real-time data synchronization
- 4Prototyping full-stack applications without building custom backend infrastructure
- 5Migrating from Firebase to maintain data sovereignty while keeping similar functionality
- 6Building internal tools requiring rapid API development from existing PostgreSQL schemas
- 7Creating content management systems with user authentication and file upload capabilities
Prerequisites
- Docker and Docker Compose with at least 4GB RAM available for all services
- Supabase CLI installed for generating JWT secrets and API keys
- Basic understanding of PostgreSQL Row Level Security and database schema design
- Kong configuration knowledge for customizing API gateway routing rules
- Ports 3000, 8000, and 8443 available on the host system
- Understanding of JWT authentication flows and token management
For development & testing. Review security settings, change default credentials, and test thoroughly before production use. See Terms
docker-compose.yml
docker-compose.yml
1services: 2 kong: 3 image: kong:2.84 container_name: supabase-kong5 environment: 6 - KONG_DATABASE=off7 - KONG_DECLARATIVE_CONFIG=/var/lib/kong/kong.yml8 - KONG_DNS_ORDER=LAST,A,CNAME9 - KONG_PLUGINS=request-transformer,cors,key-auth,acl10 volumes: 11 - ./volumes/kong/kong.yml:/var/lib/kong/kong.yml:ro12 ports: 13 - "8000:8000"14 - "8443:8443"15 networks: 16 - supabase-network1718 db: 19 image: supabase/postgres:15.1.0.11720 container_name: supabase-db21 environment: 22 - POSTGRES_HOST=/var/run/postgresql23 - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}24 volumes: 25 - postgres_data:/var/lib/postgresql/data26 healthcheck: 27 test: ["CMD", "pg_isready", "-U", "postgres"]28 interval: 5s29 timeout: 5s30 retries: 531 networks: 32 - supabase-network3334 auth: 35 image: supabase/gotrue:v2.132.336 container_name: supabase-auth37 environment: 38 - GOTRUE_API_HOST=0.0.0.039 - GOTRUE_API_PORT=999940 - API_EXTERNAL_URL=${API_EXTERNAL_URL}41 - GOTRUE_DB_DRIVER=postgres42 - GOTRUE_DB_DATABASE_URL=postgres://supabase_auth_admin:${POSTGRES_PASSWORD}@db:5432/postgres43 - GOTRUE_SITE_URL=${SITE_URL}44 - GOTRUE_JWT_SECRET=${JWT_SECRET}45 - GOTRUE_JWT_EXP=360046 depends_on: 47 db: 48 condition: service_healthy49 networks: 50 - supabase-network5152 rest: 53 image: postgrest/postgrest:v11.2.254 container_name: supabase-rest55 environment: 56 - PGRST_DB_URI=postgres://authenticator:${POSTGRES_PASSWORD}@db:5432/postgres57 - PGRST_DB_SCHEMAS=public,storage58 - PGRST_DB_ANON_ROLE=anon59 - PGRST_JWT_SECRET=${JWT_SECRET}60 depends_on: 61 db: 62 condition: service_healthy63 networks: 64 - supabase-network6566 realtime: 67 image: supabase/realtime:v2.25.3568 container_name: supabase-realtime69 environment: 70 - PORT=400071 - DB_HOST=db72 - DB_PORT=543273 - DB_USER=supabase_admin74 - DB_PASSWORD=${POSTGRES_PASSWORD}75 - DB_NAME=postgres76 - DB_AFTER_CONNECT_QUERY='SET search_path TO _realtime'77 - API_JWT_SECRET=${JWT_SECRET}78 - SECRET_KEY_BASE=${SECRET_KEY_BASE}79 depends_on: 80 db: 81 condition: service_healthy82 networks: 83 - supabase-network8485 storage: 86 image: supabase/storage-api:v0.43.1187 container_name: supabase-storage88 environment: 89 - ANON_KEY=${ANON_KEY}90 - SERVICE_KEY=${SERVICE_ROLE_KEY}91 - POSTGREST_URL=http://rest:300092 - PGRST_JWT_SECRET=${JWT_SECRET}93 - DATABASE_URL=postgres://supabase_storage_admin:${POSTGRES_PASSWORD}@db:5432/postgres94 - FILE_SIZE_LIMIT=5242880095 - STORAGE_BACKEND=file96 - FILE_STORAGE_BACKEND_PATH=/var/lib/storage97 volumes: 98 - storage_data:/var/lib/storage99 depends_on: 100 db: 101 condition: service_healthy102 networks: 103 - supabase-network104105 studio: 106 image: supabase/studio:20231123-64a766a107 container_name: supabase-studio108 environment: 109 - STUDIO_PG_META_URL=http://meta:8080110 - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}111 - SUPABASE_URL=http://kong:8000112 - SUPABASE_PUBLIC_URL=${API_EXTERNAL_URL}113 - SUPABASE_ANON_KEY=${ANON_KEY}114 - SUPABASE_SERVICE_KEY=${SERVICE_ROLE_KEY}115 ports: 116 - "3000:3000"117 depends_on: 118 - kong119 networks: 120 - supabase-network121122 meta: 123 image: supabase/postgres-meta:v0.68.0124 container_name: supabase-meta125 environment: 126 - PG_META_PORT=8080127 - PG_META_DB_HOST=db128 - PG_META_DB_PORT=5432129 - PG_META_DB_NAME=postgres130 - PG_META_DB_USER=supabase_admin131 - PG_META_DB_PASSWORD=${POSTGRES_PASSWORD}132 depends_on: 133 db: 134 condition: service_healthy135 networks: 136 - supabase-network137138volumes: 139 postgres_data: 140 storage_data: 141142networks: 143 supabase-network: 144 driver: bridge.env Template
.env
1# Supabase Self-Hosted2POSTGRES_PASSWORD=your-super-secret-password3JWT_SECRET=your-super-secret-jwt-token-with-at-least-32-characters4ANON_KEY=your-anon-key5SERVICE_ROLE_KEY=your-service-role-key6SECRET_KEY_BASE=your-secret-key-base7API_EXTERNAL_URL=http://localhost:80008SITE_URL=http://localhost:3000Usage Notes
- 1Studio UI at http://localhost:3000
- 2API Gateway at http://localhost:8000
- 3Generate keys using Supabase CLI
- 4Create volumes/kong/kong.yml config
- 5See Supabase docs for full setup
Individual Services(8 services)
Copy individual services to mix and match with your existing compose files.
kong
kong:
image: kong:2.8
container_name: supabase-kong
environment:
- KONG_DATABASE=off
- KONG_DECLARATIVE_CONFIG=/var/lib/kong/kong.yml
- KONG_DNS_ORDER=LAST,A,CNAME
- KONG_PLUGINS=request-transformer,cors,key-auth,acl
volumes:
- ./volumes/kong/kong.yml:/var/lib/kong/kong.yml:ro
ports:
- "8000:8000"
- "8443:8443"
networks:
- supabase-network
db
db:
image: supabase/postgres:15.1.0.117
container_name: supabase-db
environment:
- POSTGRES_HOST=/var/run/postgresql
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test:
- CMD
- pg_isready
- "-U"
- postgres
interval: 5s
timeout: 5s
retries: 5
networks:
- supabase-network
auth
auth:
image: supabase/gotrue:v2.132.3
container_name: supabase-auth
environment:
- GOTRUE_API_HOST=0.0.0.0
- GOTRUE_API_PORT=9999
- API_EXTERNAL_URL=${API_EXTERNAL_URL}
- GOTRUE_DB_DRIVER=postgres
- GOTRUE_DB_DATABASE_URL=postgres://supabase_auth_admin:${POSTGRES_PASSWORD}@db:5432/postgres
- GOTRUE_SITE_URL=${SITE_URL}
- GOTRUE_JWT_SECRET=${JWT_SECRET}
- GOTRUE_JWT_EXP=3600
depends_on:
db:
condition: service_healthy
networks:
- supabase-network
rest
rest:
image: postgrest/postgrest:v11.2.2
container_name: supabase-rest
environment:
- PGRST_DB_URI=postgres://authenticator:${POSTGRES_PASSWORD}@db:5432/postgres
- PGRST_DB_SCHEMAS=public,storage
- PGRST_DB_ANON_ROLE=anon
- PGRST_JWT_SECRET=${JWT_SECRET}
depends_on:
db:
condition: service_healthy
networks:
- supabase-network
realtime
realtime:
image: supabase/realtime:v2.25.35
container_name: supabase-realtime
environment:
- PORT=4000
- DB_HOST=db
- DB_PORT=5432
- DB_USER=supabase_admin
- DB_PASSWORD=${POSTGRES_PASSWORD}
- DB_NAME=postgres
- DB_AFTER_CONNECT_QUERY='SET search_path TO _realtime'
- API_JWT_SECRET=${JWT_SECRET}
- SECRET_KEY_BASE=${SECRET_KEY_BASE}
depends_on:
db:
condition: service_healthy
networks:
- supabase-network
storage
storage:
image: supabase/storage-api:v0.43.11
container_name: supabase-storage
environment:
- ANON_KEY=${ANON_KEY}
- SERVICE_KEY=${SERVICE_ROLE_KEY}
- POSTGREST_URL=http://rest:3000
- PGRST_JWT_SECRET=${JWT_SECRET}
- DATABASE_URL=postgres://supabase_storage_admin:${POSTGRES_PASSWORD}@db:5432/postgres
- FILE_SIZE_LIMIT=52428800
- STORAGE_BACKEND=file
- FILE_STORAGE_BACKEND_PATH=/var/lib/storage
volumes:
- storage_data:/var/lib/storage
depends_on:
db:
condition: service_healthy
networks:
- supabase-network
studio
studio:
image: supabase/studio:20231123-64a766a
container_name: supabase-studio
environment:
- STUDIO_PG_META_URL=http://meta:8080
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- SUPABASE_URL=http://kong:8000
- SUPABASE_PUBLIC_URL=${API_EXTERNAL_URL}
- SUPABASE_ANON_KEY=${ANON_KEY}
- SUPABASE_SERVICE_KEY=${SERVICE_ROLE_KEY}
ports:
- "3000:3000"
depends_on:
- kong
networks:
- supabase-network
meta
meta:
image: supabase/postgres-meta:v0.68.0
container_name: supabase-meta
environment:
- PG_META_PORT=8080
- PG_META_DB_HOST=db
- PG_META_DB_PORT=5432
- PG_META_DB_NAME=postgres
- PG_META_DB_USER=supabase_admin
- PG_META_DB_PASSWORD=${POSTGRES_PASSWORD}
depends_on:
db:
condition: service_healthy
networks:
- supabase-network
Quick Start
terminal
1# 1. Create the compose file2cat > docker-compose.yml << 'EOF'3services:4 kong:5 image: kong:2.86 container_name: supabase-kong7 environment:8 - KONG_DATABASE=off9 - KONG_DECLARATIVE_CONFIG=/var/lib/kong/kong.yml10 - KONG_DNS_ORDER=LAST,A,CNAME11 - KONG_PLUGINS=request-transformer,cors,key-auth,acl12 volumes:13 - ./volumes/kong/kong.yml:/var/lib/kong/kong.yml:ro14 ports:15 - "8000:8000"16 - "8443:8443"17 networks:18 - supabase-network1920 db:21 image: supabase/postgres:15.1.0.11722 container_name: supabase-db23 environment:24 - POSTGRES_HOST=/var/run/postgresql25 - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}26 volumes:27 - postgres_data:/var/lib/postgresql/data28 healthcheck:29 test: ["CMD", "pg_isready", "-U", "postgres"]30 interval: 5s31 timeout: 5s32 retries: 533 networks:34 - supabase-network3536 auth:37 image: supabase/gotrue:v2.132.338 container_name: supabase-auth39 environment:40 - GOTRUE_API_HOST=0.0.0.041 - GOTRUE_API_PORT=999942 - API_EXTERNAL_URL=${API_EXTERNAL_URL}43 - GOTRUE_DB_DRIVER=postgres44 - GOTRUE_DB_DATABASE_URL=postgres://supabase_auth_admin:${POSTGRES_PASSWORD}@db:5432/postgres45 - GOTRUE_SITE_URL=${SITE_URL}46 - GOTRUE_JWT_SECRET=${JWT_SECRET}47 - GOTRUE_JWT_EXP=360048 depends_on:49 db:50 condition: service_healthy51 networks:52 - supabase-network5354 rest:55 image: postgrest/postgrest:v11.2.256 container_name: supabase-rest57 environment:58 - PGRST_DB_URI=postgres://authenticator:${POSTGRES_PASSWORD}@db:5432/postgres59 - PGRST_DB_SCHEMAS=public,storage60 - PGRST_DB_ANON_ROLE=anon61 - PGRST_JWT_SECRET=${JWT_SECRET}62 depends_on:63 db:64 condition: service_healthy65 networks:66 - supabase-network6768 realtime:69 image: supabase/realtime:v2.25.3570 container_name: supabase-realtime71 environment:72 - PORT=400073 - DB_HOST=db74 - DB_PORT=543275 - DB_USER=supabase_admin76 - DB_PASSWORD=${POSTGRES_PASSWORD}77 - DB_NAME=postgres78 - DB_AFTER_CONNECT_QUERY='SET search_path TO _realtime'79 - API_JWT_SECRET=${JWT_SECRET}80 - SECRET_KEY_BASE=${SECRET_KEY_BASE}81 depends_on:82 db:83 condition: service_healthy84 networks:85 - supabase-network8687 storage:88 image: supabase/storage-api:v0.43.1189 container_name: supabase-storage90 environment:91 - ANON_KEY=${ANON_KEY}92 - SERVICE_KEY=${SERVICE_ROLE_KEY}93 - POSTGREST_URL=http://rest:300094 - PGRST_JWT_SECRET=${JWT_SECRET}95 - DATABASE_URL=postgres://supabase_storage_admin:${POSTGRES_PASSWORD}@db:5432/postgres96 - FILE_SIZE_LIMIT=5242880097 - STORAGE_BACKEND=file98 - FILE_STORAGE_BACKEND_PATH=/var/lib/storage99 volumes:100 - storage_data:/var/lib/storage101 depends_on:102 db:103 condition: service_healthy104 networks:105 - supabase-network106107 studio:108 image: supabase/studio:20231123-64a766a109 container_name: supabase-studio110 environment:111 - STUDIO_PG_META_URL=http://meta:8080112 - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}113 - SUPABASE_URL=http://kong:8000114 - SUPABASE_PUBLIC_URL=${API_EXTERNAL_URL}115 - SUPABASE_ANON_KEY=${ANON_KEY}116 - SUPABASE_SERVICE_KEY=${SERVICE_ROLE_KEY}117 ports:118 - "3000:3000"119 depends_on:120 - kong121 networks:122 - supabase-network123124 meta:125 image: supabase/postgres-meta:v0.68.0126 container_name: supabase-meta127 environment:128 - PG_META_PORT=8080129 - PG_META_DB_HOST=db130 - PG_META_DB_PORT=5432131 - PG_META_DB_NAME=postgres132 - PG_META_DB_USER=supabase_admin133 - PG_META_DB_PASSWORD=${POSTGRES_PASSWORD}134 depends_on:135 db:136 condition: service_healthy137 networks:138 - supabase-network139140volumes:141 postgres_data:142 storage_data:143144networks:145 supabase-network:146 driver: bridge147EOF148149# 2. Create the .env file150cat > .env << 'EOF'151# Supabase Self-Hosted152POSTGRES_PASSWORD=your-super-secret-password153JWT_SECRET=your-super-secret-jwt-token-with-at-least-32-characters154ANON_KEY=your-anon-key155SERVICE_ROLE_KEY=your-service-role-key156SECRET_KEY_BASE=your-secret-key-base157API_EXTERNAL_URL=http://localhost:8000158SITE_URL=http://localhost:3000159EOF160161# 3. Start the services162docker compose up -d163164# 4. View logs165docker compose logs -fOne-Liner
Run this command to download and set up the recipe in one step:
terminal
1curl -fsSL https://docker.recipes/api/recipes/supabase-stack/run | bashTroubleshooting
- GoTrue auth service fails to start: Ensure JWT_SECRET is exactly 32 characters and matches across all services
- PostgREST returns 401 errors: Verify the authenticator role exists in PostgreSQL and has correct permissions
- Realtime subscriptions not working: Check that the _realtime schema is properly initialized in PostgreSQL
- Kong gateway returns 502 errors: Ensure kong.yml configuration file exists and contains proper service definitions
- Storage API upload failures: Verify FILE_SIZE_LIMIT environment variable and storage bucket policies are configured
- Studio interface shows connection errors: Check that SUPABASE_URL points to Kong gateway and all API keys are valid
Community Notes
Loading...
Loading notes...
Download Recipe Kit
Get all files in a ready-to-deploy package
Includes docker-compose.yml, .env template, README, and license
Components
postgrespostgrestgotruerealtimestoragekong
Tags
#supabase#postgres#realtime#auth#backend-as-service
Category
Full Web StacksAd Space
Shortcuts: C CopyF FavoriteD Download