docker.recipes

Supabase Self-Hosted

advanced

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.8
4 container_name: supabase-kong
5 environment:
6 - KONG_DATABASE=off
7 - KONG_DECLARATIVE_CONFIG=/var/lib/kong/kong.yml
8 - KONG_DNS_ORDER=LAST,A,CNAME
9 - KONG_PLUGINS=request-transformer,cors,key-auth,acl
10 volumes:
11 - ./volumes/kong/kong.yml:/var/lib/kong/kong.yml:ro
12 ports:
13 - "8000:8000"
14 - "8443:8443"
15 networks:
16 - supabase-network
17
18 db:
19 image: supabase/postgres:15.1.0.117
20 container_name: supabase-db
21 environment:
22 - POSTGRES_HOST=/var/run/postgresql
23 - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
24 volumes:
25 - postgres_data:/var/lib/postgresql/data
26 healthcheck:
27 test: ["CMD", "pg_isready", "-U", "postgres"]
28 interval: 5s
29 timeout: 5s
30 retries: 5
31 networks:
32 - supabase-network
33
34 auth:
35 image: supabase/gotrue:v2.132.3
36 container_name: supabase-auth
37 environment:
38 - GOTRUE_API_HOST=0.0.0.0
39 - GOTRUE_API_PORT=9999
40 - API_EXTERNAL_URL=${API_EXTERNAL_URL}
41 - GOTRUE_DB_DRIVER=postgres
42 - GOTRUE_DB_DATABASE_URL=postgres://supabase_auth_admin:${POSTGRES_PASSWORD}@db:5432/postgres
43 - GOTRUE_SITE_URL=${SITE_URL}
44 - GOTRUE_JWT_SECRET=${JWT_SECRET}
45 - GOTRUE_JWT_EXP=3600
46 depends_on:
47 db:
48 condition: service_healthy
49 networks:
50 - supabase-network
51
52 rest:
53 image: postgrest/postgrest:v11.2.2
54 container_name: supabase-rest
55 environment:
56 - PGRST_DB_URI=postgres://authenticator:${POSTGRES_PASSWORD}@db:5432/postgres
57 - PGRST_DB_SCHEMAS=public,storage
58 - PGRST_DB_ANON_ROLE=anon
59 - PGRST_JWT_SECRET=${JWT_SECRET}
60 depends_on:
61 db:
62 condition: service_healthy
63 networks:
64 - supabase-network
65
66 realtime:
67 image: supabase/realtime:v2.25.35
68 container_name: supabase-realtime
69 environment:
70 - PORT=4000
71 - DB_HOST=db
72 - DB_PORT=5432
73 - DB_USER=supabase_admin
74 - DB_PASSWORD=${POSTGRES_PASSWORD}
75 - DB_NAME=postgres
76 - 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_healthy
82 networks:
83 - supabase-network
84
85 storage:
86 image: supabase/storage-api:v0.43.11
87 container_name: supabase-storage
88 environment:
89 - ANON_KEY=${ANON_KEY}
90 - SERVICE_KEY=${SERVICE_ROLE_KEY}
91 - POSTGREST_URL=http://rest:3000
92 - PGRST_JWT_SECRET=${JWT_SECRET}
93 - DATABASE_URL=postgres://supabase_storage_admin:${POSTGRES_PASSWORD}@db:5432/postgres
94 - FILE_SIZE_LIMIT=52428800
95 - STORAGE_BACKEND=file
96 - FILE_STORAGE_BACKEND_PATH=/var/lib/storage
97 volumes:
98 - storage_data:/var/lib/storage
99 depends_on:
100 db:
101 condition: service_healthy
102 networks:
103 - supabase-network
104
105 studio:
106 image: supabase/studio:20231123-64a766a
107 container_name: supabase-studio
108 environment:
109 - STUDIO_PG_META_URL=http://meta:8080
110 - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
111 - SUPABASE_URL=http://kong:8000
112 - 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 - kong
119 networks:
120 - supabase-network
121
122 meta:
123 image: supabase/postgres-meta:v0.68.0
124 container_name: supabase-meta
125 environment:
126 - PG_META_PORT=8080
127 - PG_META_DB_HOST=db
128 - PG_META_DB_PORT=5432
129 - PG_META_DB_NAME=postgres
130 - PG_META_DB_USER=supabase_admin
131 - PG_META_DB_PASSWORD=${POSTGRES_PASSWORD}
132 depends_on:
133 db:
134 condition: service_healthy
135 networks:
136 - supabase-network
137
138volumes:
139 postgres_data:
140 storage_data:
141
142networks:
143 supabase-network:
144 driver: bridge

.env Template

.env
1# Supabase Self-Hosted
2POSTGRES_PASSWORD=your-super-secret-password
3JWT_SECRET=your-super-secret-jwt-token-with-at-least-32-characters
4ANON_KEY=your-anon-key
5SERVICE_ROLE_KEY=your-service-role-key
6SECRET_KEY_BASE=your-secret-key-base
7API_EXTERNAL_URL=http://localhost:8000
8SITE_URL=http://localhost:3000

Usage Notes

  1. 1Studio UI at http://localhost:3000
  2. 2API Gateway at http://localhost:8000
  3. 3Generate keys using Supabase CLI
  4. 4Create volumes/kong/kong.yml config
  5. 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 file
2cat > docker-compose.yml << 'EOF'
3services:
4 kong:
5 image: kong:2.8
6 container_name: supabase-kong
7 environment:
8 - KONG_DATABASE=off
9 - KONG_DECLARATIVE_CONFIG=/var/lib/kong/kong.yml
10 - KONG_DNS_ORDER=LAST,A,CNAME
11 - KONG_PLUGINS=request-transformer,cors,key-auth,acl
12 volumes:
13 - ./volumes/kong/kong.yml:/var/lib/kong/kong.yml:ro
14 ports:
15 - "8000:8000"
16 - "8443:8443"
17 networks:
18 - supabase-network
19
20 db:
21 image: supabase/postgres:15.1.0.117
22 container_name: supabase-db
23 environment:
24 - POSTGRES_HOST=/var/run/postgresql
25 - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
26 volumes:
27 - postgres_data:/var/lib/postgresql/data
28 healthcheck:
29 test: ["CMD", "pg_isready", "-U", "postgres"]
30 interval: 5s
31 timeout: 5s
32 retries: 5
33 networks:
34 - supabase-network
35
36 auth:
37 image: supabase/gotrue:v2.132.3
38 container_name: supabase-auth
39 environment:
40 - GOTRUE_API_HOST=0.0.0.0
41 - GOTRUE_API_PORT=9999
42 - API_EXTERNAL_URL=${API_EXTERNAL_URL}
43 - GOTRUE_DB_DRIVER=postgres
44 - GOTRUE_DB_DATABASE_URL=postgres://supabase_auth_admin:${POSTGRES_PASSWORD}@db:5432/postgres
45 - GOTRUE_SITE_URL=${SITE_URL}
46 - GOTRUE_JWT_SECRET=${JWT_SECRET}
47 - GOTRUE_JWT_EXP=3600
48 depends_on:
49 db:
50 condition: service_healthy
51 networks:
52 - supabase-network
53
54 rest:
55 image: postgrest/postgrest:v11.2.2
56 container_name: supabase-rest
57 environment:
58 - PGRST_DB_URI=postgres://authenticator:${POSTGRES_PASSWORD}@db:5432/postgres
59 - PGRST_DB_SCHEMAS=public,storage
60 - PGRST_DB_ANON_ROLE=anon
61 - PGRST_JWT_SECRET=${JWT_SECRET}
62 depends_on:
63 db:
64 condition: service_healthy
65 networks:
66 - supabase-network
67
68 realtime:
69 image: supabase/realtime:v2.25.35
70 container_name: supabase-realtime
71 environment:
72 - PORT=4000
73 - DB_HOST=db
74 - DB_PORT=5432
75 - DB_USER=supabase_admin
76 - DB_PASSWORD=${POSTGRES_PASSWORD}
77 - DB_NAME=postgres
78 - 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_healthy
84 networks:
85 - supabase-network
86
87 storage:
88 image: supabase/storage-api:v0.43.11
89 container_name: supabase-storage
90 environment:
91 - ANON_KEY=${ANON_KEY}
92 - SERVICE_KEY=${SERVICE_ROLE_KEY}
93 - POSTGREST_URL=http://rest:3000
94 - PGRST_JWT_SECRET=${JWT_SECRET}
95 - DATABASE_URL=postgres://supabase_storage_admin:${POSTGRES_PASSWORD}@db:5432/postgres
96 - FILE_SIZE_LIMIT=52428800
97 - STORAGE_BACKEND=file
98 - FILE_STORAGE_BACKEND_PATH=/var/lib/storage
99 volumes:
100 - storage_data:/var/lib/storage
101 depends_on:
102 db:
103 condition: service_healthy
104 networks:
105 - supabase-network
106
107 studio:
108 image: supabase/studio:20231123-64a766a
109 container_name: supabase-studio
110 environment:
111 - STUDIO_PG_META_URL=http://meta:8080
112 - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
113 - SUPABASE_URL=http://kong:8000
114 - 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 - kong
121 networks:
122 - supabase-network
123
124 meta:
125 image: supabase/postgres-meta:v0.68.0
126 container_name: supabase-meta
127 environment:
128 - PG_META_PORT=8080
129 - PG_META_DB_HOST=db
130 - PG_META_DB_PORT=5432
131 - PG_META_DB_NAME=postgres
132 - PG_META_DB_USER=supabase_admin
133 - PG_META_DB_PASSWORD=${POSTGRES_PASSWORD}
134 depends_on:
135 db:
136 condition: service_healthy
137 networks:
138 - supabase-network
139
140volumes:
141 postgres_data:
142 storage_data:
143
144networks:
145 supabase-network:
146 driver: bridge
147EOF
148
149# 2. Create the .env file
150cat > .env << 'EOF'
151# Supabase Self-Hosted
152POSTGRES_PASSWORD=your-super-secret-password
153JWT_SECRET=your-super-secret-jwt-token-with-at-least-32-characters
154ANON_KEY=your-anon-key
155SERVICE_ROLE_KEY=your-service-role-key
156SECRET_KEY_BASE=your-secret-key-base
157API_EXTERNAL_URL=http://localhost:8000
158SITE_URL=http://localhost:3000
159EOF
160
161# 3. Start the services
162docker compose up -d
163
164# 4. View logs
165docker 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-stack/run | bash

Troubleshooting

  • 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

Ad Space