Supabase (Self-Hosted)
Open-source Firebase alternative with Postgres, Auth, Storage, and Realtime subscriptions
Overview
Supabase is an open-source Backend-as-a-Service (BaaS) platform that positions itself as a Firebase alternative, built on top of PostgreSQL with a suite of complementary tools. Originally created by Paul Copplestone and Ant Wilson in 2020, Supabase provides developers with authentication, real-time subscriptions, file storage, and auto-generated APIs without sacrificing the power and flexibility of SQL. Unlike Firebase's NoSQL approach, Supabase leverages PostgreSQL's mature ecosystem while adding modern developer experience features like real-time capabilities and instant APIs.
This self-hosted stack orchestrates seven specialized components to deliver a complete backend platform: Supabase Studio provides the web-based dashboard for database management and API testing, while PostgreSQL serves as the robust foundation with built-in Row Level Security. Kong Gateway acts as the intelligent API proxy, routing requests between GoTrue for authentication, PostgREST for auto-generated REST APIs, Realtime for WebSocket subscriptions, and Storage API for file management. The postgres-meta service enables Studio's database introspection capabilities, creating a unified development experience.
This configuration targets teams seeking Firebase-like developer experience while maintaining full control over their data and infrastructure. Startups can rapidly prototype applications with built-in authentication and real-time features, while enterprises benefit from PostgreSQL's ACID compliance, advanced querying capabilities, and the ability to customize every component. The self-hosted approach eliminates vendor lock-in concerns and allows for compliance with strict data residency requirements that SaaS solutions cannot accommodate.
Key Features
- Auto-generated REST and GraphQL APIs from PostgreSQL schema via PostgREST with automatic OpenAPI documentation
- Real-time subscriptions using PostgreSQL's logical replication with WebSocket broadcasting for live data updates
- Row Level Security (RLS) policies directly in PostgreSQL for fine-grained access control without application-level permissions
- Multi-provider authentication through GoTrue supporting email, OAuth providers, and magic links with JWT token management
- File storage with automatic image transformations, resumable uploads, and PostgreSQL metadata integration
- Database management GUI through Studio with visual schema editor, SQL query interface, and API testing playground
- Kong Gateway routing with rate limiting, CORS handling, and request transformation for production API management
- Built-in database functions and triggers support for complex business logic execution at the database layer
Common Use Cases
- 1SaaS applications requiring user authentication, file uploads, and real-time collaboration features like document editing or chat
- 2E-commerce platforms needing inventory management with real-time stock updates and secure payment processing workflows
- 3Content management systems with multi-user editing, media storage, and role-based access control requirements
- 4IoT dashboards collecting sensor data with real-time visualization and historical analytics stored in PostgreSQL
- 5Multi-tenant applications using PostgreSQL schemas or RLS policies for complete data isolation between customers
- 6Compliance-sensitive applications in healthcare, finance, or government requiring on-premises data storage
- 7Development teams migrating from Firebase who need SQL capabilities while preserving real-time functionality
Prerequisites
- Minimum 4GB RAM and 20GB storage for the complete stack with PostgreSQL, Kong, and all Supabase services
- Docker and Docker Compose v2+ with ability to bind to ports 3000, 5432, 8000, and 8001 on the host system
- Generated JWT secret key (32+ characters) and Supabase API keys using supabase CLI or online JWT generator
- Kong declarative configuration file created at ./volumes/kong/kong.yml with service routing definitions
- Basic PostgreSQL knowledge for schema design, RLS policies, and understanding of database user roles
- Familiarity with REST API concepts and JWT authentication for integration with client applications
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 studio: 3 image: supabase/studio:latest4 container_name: supabase-studio5 restart: unless-stopped6 ports: 7 - "${STUDIO_PORT:-3000}:3000"8 environment: 9 - STUDIO_PG_META_URL=http://meta:808010 - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}11 - DEFAULT_ORGANIZATION_NAME=Default12 - DEFAULT_PROJECT_NAME=Default13 - SUPABASE_URL=http://kong:800014 - SUPABASE_PUBLIC_URL=http://localhost:${KONG_PORT:-8000}15 - SUPABASE_ANON_KEY=${ANON_KEY}16 - SUPABASE_SERVICE_KEY=${SERVICE_ROLE_KEY}17 depends_on: 18 - meta1920 kong: 21 image: kong:2.8.122 container_name: supabase-kong23 restart: unless-stopped24 ports: 25 - "${KONG_PORT:-8000}:8000"26 - "${KONG_ADMIN_PORT:-8001}:8001"27 environment: 28 - KONG_DATABASE=off29 - KONG_DECLARATIVE_CONFIG=/var/lib/kong/kong.yml30 - KONG_DNS_ORDER=LAST,A,CNAME31 - KONG_PLUGINS=request-transformer,cors,key-auth,acl32 volumes: 33 - ./volumes/kong/kong.yml:/var/lib/kong/kong.yml:ro3435 auth: 36 image: supabase/gotrue:latest37 container_name: supabase-auth38 restart: unless-stopped39 environment: 40 - GOTRUE_API_HOST=0.0.0.041 - GOTRUE_API_PORT=999942 - GOTRUE_DB_DRIVER=postgres43 - GOTRUE_DB_DATABASE_URL=postgres://supabase_auth_admin:${POSTGRES_PASSWORD}@db:5432/postgres44 - GOTRUE_SITE_URL=${SITE_URL:-http://localhost:3000}45 - GOTRUE_JWT_SECRET=${JWT_SECRET}46 - GOTRUE_JWT_EXP=360047 depends_on: 48 - db4950 realtime: 51 image: supabase/realtime:latest52 container_name: supabase-realtime53 restart: unless-stopped54 environment: 55 - PORT=400056 - DB_HOST=db57 - DB_PORT=543258 - DB_USER=supabase_admin59 - DB_PASSWORD=${POSTGRES_PASSWORD}60 - DB_NAME=postgres61 - DB_AFTER_CONNECT_QUERY=SET search_path TO _realtime62 - DB_ENC_KEY=supabasekey63 - API_JWT_SECRET=${JWT_SECRET}64 - SECRET_KEY_BASE=${SECRET_KEY_BASE}65 depends_on: 66 - db6768 storage: 69 image: supabase/storage-api:latest70 container_name: supabase-storage71 restart: unless-stopped72 environment: 73 - ANON_KEY=${ANON_KEY}74 - SERVICE_KEY=${SERVICE_ROLE_KEY}75 - POSTGREST_URL=http://rest:300076 - PGRST_JWT_SECRET=${JWT_SECRET}77 - DATABASE_URL=postgres://supabase_storage_admin:${POSTGRES_PASSWORD}@db:5432/postgres78 - STORAGE_BACKEND=file79 - FILE_STORAGE_BACKEND_PATH=/var/lib/storage80 - TENANT_ID=stub81 - REGION=stub82 - GLOBAL_S3_BUCKET=stub83 volumes: 84 - ./volumes/storage:/var/lib/storage85 depends_on: 86 - db87 - rest8889 rest: 90 image: postgrest/postgrest:latest91 container_name: supabase-rest92 restart: unless-stopped93 environment: 94 - PGRST_DB_URI=postgres://authenticator:${POSTGRES_PASSWORD}@db:5432/postgres95 - PGRST_DB_SCHEMAS=public,storage,graphql_public96 - PGRST_DB_ANON_ROLE=anon97 - PGRST_JWT_SECRET=${JWT_SECRET}98 depends_on: 99 - db100101 meta: 102 image: supabase/postgres-meta:latest103 container_name: supabase-meta104 restart: unless-stopped105 environment: 106 - PG_META_PORT=8080107 - PG_META_DB_HOST=db108 - PG_META_DB_PORT=5432109 - PG_META_DB_NAME=postgres110 - PG_META_DB_USER=supabase_admin111 - PG_META_DB_PASSWORD=${POSTGRES_PASSWORD}112 depends_on: 113 - db114115 db: 116 image: supabase/postgres:15.1.0.117117 container_name: supabase-db118 restart: unless-stopped119 ports: 120 - "${DB_PORT:-5432}:5432"121 environment: 122 - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}123 - POSTGRES_HOST=/var/run/postgresql124 volumes: 125 - ./volumes/db/data:/var/lib/postgresql/data126 healthcheck: 127 test: pg_isready -U postgres -h localhost128 interval: 5s129 timeout: 5s130 retries: 10.env Template
.env
1# Supabase Configuration2STUDIO_PORT=30003KONG_PORT=80004DB_PORT=543256# Database password7POSTGRES_PASSWORD=your-super-secret-password89# JWT Secret (generate with: openssl rand -base64 32)10JWT_SECRET=your-jwt-secret-at-least-32-characters1112# Secret key base for Realtime13SECRET_KEY_BASE=your-secret-key-base-at-least-64-characters1415# API Keys (generate these or use Supabase CLI)16ANON_KEY=your-anon-key17SERVICE_ROLE_KEY=your-service-role-key1819# Site URL20SITE_URL=http://localhost:3000Usage Notes
- 1Studio dashboard at http://localhost:3000
- 2API endpoint at http://localhost:8000
- 3Direct Postgres access at localhost:5432
- 4Generate JWT keys using Supabase CLI or online generator
- 5Create Kong config at ./volumes/kong/kong.yml
- 6See github.com/supabase/supabase/docker for full setup
Individual Services(8 services)
Copy individual services to mix and match with your existing compose files.
studio
studio:
image: supabase/studio:latest
container_name: supabase-studio
restart: unless-stopped
ports:
- ${STUDIO_PORT:-3000}:3000
environment:
- STUDIO_PG_META_URL=http://meta:8080
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- DEFAULT_ORGANIZATION_NAME=Default
- DEFAULT_PROJECT_NAME=Default
- SUPABASE_URL=http://kong:8000
- SUPABASE_PUBLIC_URL=http://localhost:${KONG_PORT:-8000}
- SUPABASE_ANON_KEY=${ANON_KEY}
- SUPABASE_SERVICE_KEY=${SERVICE_ROLE_KEY}
depends_on:
- meta
kong
kong:
image: kong:2.8.1
container_name: supabase-kong
restart: unless-stopped
ports:
- ${KONG_PORT:-8000}:8000
- ${KONG_ADMIN_PORT:-8001}:8001
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
auth
auth:
image: supabase/gotrue:latest
container_name: supabase-auth
restart: unless-stopped
environment:
- GOTRUE_API_HOST=0.0.0.0
- GOTRUE_API_PORT=9999
- GOTRUE_DB_DRIVER=postgres
- GOTRUE_DB_DATABASE_URL=postgres://supabase_auth_admin:${POSTGRES_PASSWORD}@db:5432/postgres
- GOTRUE_SITE_URL=${SITE_URL:-http://localhost:3000}
- GOTRUE_JWT_SECRET=${JWT_SECRET}
- GOTRUE_JWT_EXP=3600
depends_on:
- db
realtime
realtime:
image: supabase/realtime:latest
container_name: supabase-realtime
restart: unless-stopped
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
- DB_ENC_KEY=supabasekey
- API_JWT_SECRET=${JWT_SECRET}
- SECRET_KEY_BASE=${SECRET_KEY_BASE}
depends_on:
- db
storage
storage:
image: supabase/storage-api:latest
container_name: supabase-storage
restart: unless-stopped
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
- STORAGE_BACKEND=file
- FILE_STORAGE_BACKEND_PATH=/var/lib/storage
- TENANT_ID=stub
- REGION=stub
- GLOBAL_S3_BUCKET=stub
volumes:
- ./volumes/storage:/var/lib/storage
depends_on:
- db
- rest
rest
rest:
image: postgrest/postgrest:latest
container_name: supabase-rest
restart: unless-stopped
environment:
- PGRST_DB_URI=postgres://authenticator:${POSTGRES_PASSWORD}@db:5432/postgres
- PGRST_DB_SCHEMAS=public,storage,graphql_public
- PGRST_DB_ANON_ROLE=anon
- PGRST_JWT_SECRET=${JWT_SECRET}
depends_on:
- db
meta
meta:
image: supabase/postgres-meta:latest
container_name: supabase-meta
restart: unless-stopped
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
db
db:
image: supabase/postgres:15.1.0.117
container_name: supabase-db
restart: unless-stopped
ports:
- ${DB_PORT:-5432}:5432
environment:
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_HOST=/var/run/postgresql
volumes:
- ./volumes/db/data:/var/lib/postgresql/data
healthcheck:
test: pg_isready -U postgres -h localhost
interval: 5s
timeout: 5s
retries: 10
Quick Start
terminal
1# 1. Create the compose file2cat > docker-compose.yml << 'EOF'3services:4 studio:5 image: supabase/studio:latest6 container_name: supabase-studio7 restart: unless-stopped8 ports:9 - "${STUDIO_PORT:-3000}:3000"10 environment:11 - STUDIO_PG_META_URL=http://meta:808012 - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}13 - DEFAULT_ORGANIZATION_NAME=Default14 - DEFAULT_PROJECT_NAME=Default15 - SUPABASE_URL=http://kong:800016 - SUPABASE_PUBLIC_URL=http://localhost:${KONG_PORT:-8000}17 - SUPABASE_ANON_KEY=${ANON_KEY}18 - SUPABASE_SERVICE_KEY=${SERVICE_ROLE_KEY}19 depends_on:20 - meta2122 kong:23 image: kong:2.8.124 container_name: supabase-kong25 restart: unless-stopped26 ports:27 - "${KONG_PORT:-8000}:8000"28 - "${KONG_ADMIN_PORT:-8001}:8001"29 environment:30 - KONG_DATABASE=off31 - KONG_DECLARATIVE_CONFIG=/var/lib/kong/kong.yml32 - KONG_DNS_ORDER=LAST,A,CNAME33 - KONG_PLUGINS=request-transformer,cors,key-auth,acl34 volumes:35 - ./volumes/kong/kong.yml:/var/lib/kong/kong.yml:ro3637 auth:38 image: supabase/gotrue:latest39 container_name: supabase-auth40 restart: unless-stopped41 environment:42 - GOTRUE_API_HOST=0.0.0.043 - GOTRUE_API_PORT=999944 - GOTRUE_DB_DRIVER=postgres45 - GOTRUE_DB_DATABASE_URL=postgres://supabase_auth_admin:${POSTGRES_PASSWORD}@db:5432/postgres46 - GOTRUE_SITE_URL=${SITE_URL:-http://localhost:3000}47 - GOTRUE_JWT_SECRET=${JWT_SECRET}48 - GOTRUE_JWT_EXP=360049 depends_on:50 - db5152 realtime:53 image: supabase/realtime:latest54 container_name: supabase-realtime55 restart: unless-stopped56 environment:57 - PORT=400058 - DB_HOST=db59 - DB_PORT=543260 - DB_USER=supabase_admin61 - DB_PASSWORD=${POSTGRES_PASSWORD}62 - DB_NAME=postgres63 - DB_AFTER_CONNECT_QUERY=SET search_path TO _realtime64 - DB_ENC_KEY=supabasekey65 - API_JWT_SECRET=${JWT_SECRET}66 - SECRET_KEY_BASE=${SECRET_KEY_BASE}67 depends_on:68 - db6970 storage:71 image: supabase/storage-api:latest72 container_name: supabase-storage73 restart: unless-stopped74 environment:75 - ANON_KEY=${ANON_KEY}76 - SERVICE_KEY=${SERVICE_ROLE_KEY}77 - POSTGREST_URL=http://rest:300078 - PGRST_JWT_SECRET=${JWT_SECRET}79 - DATABASE_URL=postgres://supabase_storage_admin:${POSTGRES_PASSWORD}@db:5432/postgres80 - STORAGE_BACKEND=file81 - FILE_STORAGE_BACKEND_PATH=/var/lib/storage82 - TENANT_ID=stub83 - REGION=stub84 - GLOBAL_S3_BUCKET=stub85 volumes:86 - ./volumes/storage:/var/lib/storage87 depends_on:88 - db89 - rest9091 rest:92 image: postgrest/postgrest:latest93 container_name: supabase-rest94 restart: unless-stopped95 environment:96 - PGRST_DB_URI=postgres://authenticator:${POSTGRES_PASSWORD}@db:5432/postgres97 - PGRST_DB_SCHEMAS=public,storage,graphql_public98 - PGRST_DB_ANON_ROLE=anon99 - PGRST_JWT_SECRET=${JWT_SECRET}100 depends_on:101 - db102103 meta:104 image: supabase/postgres-meta:latest105 container_name: supabase-meta106 restart: unless-stopped107 environment:108 - PG_META_PORT=8080109 - PG_META_DB_HOST=db110 - PG_META_DB_PORT=5432111 - PG_META_DB_NAME=postgres112 - PG_META_DB_USER=supabase_admin113 - PG_META_DB_PASSWORD=${POSTGRES_PASSWORD}114 depends_on:115 - db116117 db:118 image: supabase/postgres:15.1.0.117119 container_name: supabase-db120 restart: unless-stopped121 ports:122 - "${DB_PORT:-5432}:5432"123 environment:124 - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}125 - POSTGRES_HOST=/var/run/postgresql126 volumes:127 - ./volumes/db/data:/var/lib/postgresql/data128 healthcheck:129 test: pg_isready -U postgres -h localhost130 interval: 5s131 timeout: 5s132 retries: 10133EOF134135# 2. Create the .env file136cat > .env << 'EOF'137# Supabase Configuration138STUDIO_PORT=3000139KONG_PORT=8000140DB_PORT=5432141142# Database password143POSTGRES_PASSWORD=your-super-secret-password144145# JWT Secret (generate with: openssl rand -base64 32)146JWT_SECRET=your-jwt-secret-at-least-32-characters147148# Secret key base for Realtime149SECRET_KEY_BASE=your-secret-key-base-at-least-64-characters150151# API Keys (generate these or use Supabase CLI)152ANON_KEY=your-anon-key153SERVICE_ROLE_KEY=your-service-role-key154155# Site URL156SITE_URL=http://localhost:3000157EOF158159# 3. Start the services160docker compose up -d161162# 4. View logs163docker 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/run | bashTroubleshooting
- Studio shows 'Failed to connect to Postgres' error: Verify POSTGRES_PASSWORD matches between studio and db services, check that postgres-meta container is running and accessible
- Kong returns 503 Service Unavailable: Ensure kong.yml configuration file exists and contains valid service definitions for auth, rest, realtime, and storage endpoints
- GoTrue authentication fails with invalid JWT: Regenerate JWT_SECRET ensuring it matches across auth, rest, realtime, and storage services, restart all dependent containers
- Real-time subscriptions not working: Check that _realtime schema exists in PostgreSQL and DB_ENC_KEY is set correctly in realtime service environment
- Storage API file uploads return 400 errors: Verify storage volume permissions and ensure postgrest service is accessible from storage container
- Studio dashboard loads but shows empty database: Confirm supabase_admin user has proper permissions and postgres-meta can connect with correct credentials
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
supabase-studiopostgreskonggotruerealtimestorage
Tags
#baas#postgres#auth#storage#realtime#api
Category
Full Web StacksAd Space
Shortcuts: C CopyF FavoriteD Download