docker.recipes

Supabase (Self-Hosted)

advanced

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:latest
4 container_name: supabase-studio
5 restart: unless-stopped
6 ports:
7 - "${STUDIO_PORT:-3000}:3000"
8 environment:
9 - STUDIO_PG_META_URL=http://meta:8080
10 - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
11 - DEFAULT_ORGANIZATION_NAME=Default
12 - DEFAULT_PROJECT_NAME=Default
13 - SUPABASE_URL=http://kong:8000
14 - 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 - meta
19
20 kong:
21 image: kong:2.8.1
22 container_name: supabase-kong
23 restart: unless-stopped
24 ports:
25 - "${KONG_PORT:-8000}:8000"
26 - "${KONG_ADMIN_PORT:-8001}:8001"
27 environment:
28 - KONG_DATABASE=off
29 - KONG_DECLARATIVE_CONFIG=/var/lib/kong/kong.yml
30 - KONG_DNS_ORDER=LAST,A,CNAME
31 - KONG_PLUGINS=request-transformer,cors,key-auth,acl
32 volumes:
33 - ./volumes/kong/kong.yml:/var/lib/kong/kong.yml:ro
34
35 auth:
36 image: supabase/gotrue:latest
37 container_name: supabase-auth
38 restart: unless-stopped
39 environment:
40 - GOTRUE_API_HOST=0.0.0.0
41 - GOTRUE_API_PORT=9999
42 - GOTRUE_DB_DRIVER=postgres
43 - GOTRUE_DB_DATABASE_URL=postgres://supabase_auth_admin:${POSTGRES_PASSWORD}@db:5432/postgres
44 - GOTRUE_SITE_URL=${SITE_URL:-http://localhost:3000}
45 - GOTRUE_JWT_SECRET=${JWT_SECRET}
46 - GOTRUE_JWT_EXP=3600
47 depends_on:
48 - db
49
50 realtime:
51 image: supabase/realtime:latest
52 container_name: supabase-realtime
53 restart: unless-stopped
54 environment:
55 - PORT=4000
56 - DB_HOST=db
57 - DB_PORT=5432
58 - DB_USER=supabase_admin
59 - DB_PASSWORD=${POSTGRES_PASSWORD}
60 - DB_NAME=postgres
61 - DB_AFTER_CONNECT_QUERY=SET search_path TO _realtime
62 - DB_ENC_KEY=supabasekey
63 - API_JWT_SECRET=${JWT_SECRET}
64 - SECRET_KEY_BASE=${SECRET_KEY_BASE}
65 depends_on:
66 - db
67
68 storage:
69 image: supabase/storage-api:latest
70 container_name: supabase-storage
71 restart: unless-stopped
72 environment:
73 - ANON_KEY=${ANON_KEY}
74 - SERVICE_KEY=${SERVICE_ROLE_KEY}
75 - POSTGREST_URL=http://rest:3000
76 - PGRST_JWT_SECRET=${JWT_SECRET}
77 - DATABASE_URL=postgres://supabase_storage_admin:${POSTGRES_PASSWORD}@db:5432/postgres
78 - STORAGE_BACKEND=file
79 - FILE_STORAGE_BACKEND_PATH=/var/lib/storage
80 - TENANT_ID=stub
81 - REGION=stub
82 - GLOBAL_S3_BUCKET=stub
83 volumes:
84 - ./volumes/storage:/var/lib/storage
85 depends_on:
86 - db
87 - rest
88
89 rest:
90 image: postgrest/postgrest:latest
91 container_name: supabase-rest
92 restart: unless-stopped
93 environment:
94 - PGRST_DB_URI=postgres://authenticator:${POSTGRES_PASSWORD}@db:5432/postgres
95 - PGRST_DB_SCHEMAS=public,storage,graphql_public
96 - PGRST_DB_ANON_ROLE=anon
97 - PGRST_JWT_SECRET=${JWT_SECRET}
98 depends_on:
99 - db
100
101 meta:
102 image: supabase/postgres-meta:latest
103 container_name: supabase-meta
104 restart: unless-stopped
105 environment:
106 - PG_META_PORT=8080
107 - PG_META_DB_HOST=db
108 - PG_META_DB_PORT=5432
109 - PG_META_DB_NAME=postgres
110 - PG_META_DB_USER=supabase_admin
111 - PG_META_DB_PASSWORD=${POSTGRES_PASSWORD}
112 depends_on:
113 - db
114
115 db:
116 image: supabase/postgres:15.1.0.117
117 container_name: supabase-db
118 restart: unless-stopped
119 ports:
120 - "${DB_PORT:-5432}:5432"
121 environment:
122 - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
123 - POSTGRES_HOST=/var/run/postgresql
124 volumes:
125 - ./volumes/db/data:/var/lib/postgresql/data
126 healthcheck:
127 test: pg_isready -U postgres -h localhost
128 interval: 5s
129 timeout: 5s
130 retries: 10

.env Template

.env
1# Supabase Configuration
2STUDIO_PORT=3000
3KONG_PORT=8000
4DB_PORT=5432
5
6# Database password
7POSTGRES_PASSWORD=your-super-secret-password
8
9# JWT Secret (generate with: openssl rand -base64 32)
10JWT_SECRET=your-jwt-secret-at-least-32-characters
11
12# Secret key base for Realtime
13SECRET_KEY_BASE=your-secret-key-base-at-least-64-characters
14
15# API Keys (generate these or use Supabase CLI)
16ANON_KEY=your-anon-key
17SERVICE_ROLE_KEY=your-service-role-key
18
19# Site URL
20SITE_URL=http://localhost:3000

Usage Notes

  1. 1Studio dashboard at http://localhost:3000
  2. 2API endpoint at http://localhost:8000
  3. 3Direct Postgres access at localhost:5432
  4. 4Generate JWT keys using Supabase CLI or online generator
  5. 5Create Kong config at ./volumes/kong/kong.yml
  6. 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 file
2cat > docker-compose.yml << 'EOF'
3services:
4 studio:
5 image: supabase/studio:latest
6 container_name: supabase-studio
7 restart: unless-stopped
8 ports:
9 - "${STUDIO_PORT:-3000}:3000"
10 environment:
11 - STUDIO_PG_META_URL=http://meta:8080
12 - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
13 - DEFAULT_ORGANIZATION_NAME=Default
14 - DEFAULT_PROJECT_NAME=Default
15 - SUPABASE_URL=http://kong:8000
16 - 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 - meta
21
22 kong:
23 image: kong:2.8.1
24 container_name: supabase-kong
25 restart: unless-stopped
26 ports:
27 - "${KONG_PORT:-8000}:8000"
28 - "${KONG_ADMIN_PORT:-8001}:8001"
29 environment:
30 - KONG_DATABASE=off
31 - KONG_DECLARATIVE_CONFIG=/var/lib/kong/kong.yml
32 - KONG_DNS_ORDER=LAST,A,CNAME
33 - KONG_PLUGINS=request-transformer,cors,key-auth,acl
34 volumes:
35 - ./volumes/kong/kong.yml:/var/lib/kong/kong.yml:ro
36
37 auth:
38 image: supabase/gotrue:latest
39 container_name: supabase-auth
40 restart: unless-stopped
41 environment:
42 - GOTRUE_API_HOST=0.0.0.0
43 - GOTRUE_API_PORT=9999
44 - GOTRUE_DB_DRIVER=postgres
45 - GOTRUE_DB_DATABASE_URL=postgres://supabase_auth_admin:${POSTGRES_PASSWORD}@db:5432/postgres
46 - GOTRUE_SITE_URL=${SITE_URL:-http://localhost:3000}
47 - GOTRUE_JWT_SECRET=${JWT_SECRET}
48 - GOTRUE_JWT_EXP=3600
49 depends_on:
50 - db
51
52 realtime:
53 image: supabase/realtime:latest
54 container_name: supabase-realtime
55 restart: unless-stopped
56 environment:
57 - PORT=4000
58 - DB_HOST=db
59 - DB_PORT=5432
60 - DB_USER=supabase_admin
61 - DB_PASSWORD=${POSTGRES_PASSWORD}
62 - DB_NAME=postgres
63 - DB_AFTER_CONNECT_QUERY=SET search_path TO _realtime
64 - DB_ENC_KEY=supabasekey
65 - API_JWT_SECRET=${JWT_SECRET}
66 - SECRET_KEY_BASE=${SECRET_KEY_BASE}
67 depends_on:
68 - db
69
70 storage:
71 image: supabase/storage-api:latest
72 container_name: supabase-storage
73 restart: unless-stopped
74 environment:
75 - ANON_KEY=${ANON_KEY}
76 - SERVICE_KEY=${SERVICE_ROLE_KEY}
77 - POSTGREST_URL=http://rest:3000
78 - PGRST_JWT_SECRET=${JWT_SECRET}
79 - DATABASE_URL=postgres://supabase_storage_admin:${POSTGRES_PASSWORD}@db:5432/postgres
80 - STORAGE_BACKEND=file
81 - FILE_STORAGE_BACKEND_PATH=/var/lib/storage
82 - TENANT_ID=stub
83 - REGION=stub
84 - GLOBAL_S3_BUCKET=stub
85 volumes:
86 - ./volumes/storage:/var/lib/storage
87 depends_on:
88 - db
89 - rest
90
91 rest:
92 image: postgrest/postgrest:latest
93 container_name: supabase-rest
94 restart: unless-stopped
95 environment:
96 - PGRST_DB_URI=postgres://authenticator:${POSTGRES_PASSWORD}@db:5432/postgres
97 - PGRST_DB_SCHEMAS=public,storage,graphql_public
98 - PGRST_DB_ANON_ROLE=anon
99 - PGRST_JWT_SECRET=${JWT_SECRET}
100 depends_on:
101 - db
102
103 meta:
104 image: supabase/postgres-meta:latest
105 container_name: supabase-meta
106 restart: unless-stopped
107 environment:
108 - PG_META_PORT=8080
109 - PG_META_DB_HOST=db
110 - PG_META_DB_PORT=5432
111 - PG_META_DB_NAME=postgres
112 - PG_META_DB_USER=supabase_admin
113 - PG_META_DB_PASSWORD=${POSTGRES_PASSWORD}
114 depends_on:
115 - db
116
117 db:
118 image: supabase/postgres:15.1.0.117
119 container_name: supabase-db
120 restart: unless-stopped
121 ports:
122 - "${DB_PORT:-5432}:5432"
123 environment:
124 - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
125 - POSTGRES_HOST=/var/run/postgresql
126 volumes:
127 - ./volumes/db/data:/var/lib/postgresql/data
128 healthcheck:
129 test: pg_isready -U postgres -h localhost
130 interval: 5s
131 timeout: 5s
132 retries: 10
133EOF
134
135# 2. Create the .env file
136cat > .env << 'EOF'
137# Supabase Configuration
138STUDIO_PORT=3000
139KONG_PORT=8000
140DB_PORT=5432
141
142# Database password
143POSTGRES_PASSWORD=your-super-secret-password
144
145# JWT Secret (generate with: openssl rand -base64 32)
146JWT_SECRET=your-jwt-secret-at-least-32-characters
147
148# Secret key base for Realtime
149SECRET_KEY_BASE=your-secret-key-base-at-least-64-characters
150
151# API Keys (generate these or use Supabase CLI)
152ANON_KEY=your-anon-key
153SERVICE_ROLE_KEY=your-service-role-key
154
155# Site URL
156SITE_URL=http://localhost:3000
157EOF
158
159# 3. Start the services
160docker compose up -d
161
162# 4. View logs
163docker compose logs -f

One-Liner

Run this command to download and set up the recipe in one step:

terminal
1curl -fsSL https://docker.recipes/api/recipes/supabase/run | bash

Troubleshooting

  • 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

Ad Space