docker.recipes

Next.js + Supabase Stack

intermediate

Full-stack Next.js application with self-hosted Supabase backend.

Overview

Next.js is a production-ready React framework that enables server-side rendering, static site generation, and full-stack development capabilities with built-in API routes. Combined with Supabase's open-source backend-as-a-service platform, it creates a powerful full-stack development environment that rivals Firebase but with the flexibility of self-hosting and PostgreSQL as the underlying database. This deployment creates a complete Supabase backend infrastructure alongside a Next.js frontend application. The stack includes seven specialized services: a Next.js application server, Kong API gateway for routing, GoTrue for authentication, PostgREST for automatic API generation, Realtime for WebSocket connections, Storage API for file management, and a PostgreSQL database. Kong acts as the central API gateway, routing requests from the Next.js app to the appropriate Supabase microservices, creating a cohesive development experience. This configuration is ideal for developers who want the modern development experience of Supabase without vendor lock-in, teams building real-time applications that need instant database synchronization, and organizations requiring full control over their backend infrastructure. The combination provides type-safe database access, real-time subscriptions, user authentication, and file storage while maintaining the performance benefits of Next.js's hybrid rendering capabilities.

Key Features

  • GoTrue authentication service with JWT token management and social provider support
  • PostgREST automatic API generation that creates RESTful endpoints directly from PostgreSQL schema
  • Real-time database subscriptions through WebSocket connections for instant UI updates
  • Supabase Storage API with file upload, transformation, and CDN capabilities
  • Kong API gateway providing centralized routing and request management across all Supabase services
  • Next.js server-side rendering with automatic API route integration to Supabase backend
  • PostgreSQL database with Supabase extensions for row-level security and real-time functionality
  • Type-safe database queries through auto-generated TypeScript definitions from schema

Common Use Cases

  • 1Real-time collaborative applications like document editors, chat platforms, or project management tools
  • 2SaaS applications requiring multi-tenant architecture with row-level security policies
  • 3E-commerce platforms needing user authentication, product catalogs, and file storage for images
  • 4Content management systems with real-time editing and media asset management
  • 5Social media applications requiring user profiles, real-time feeds, and image uploads
  • 6Educational platforms with user progress tracking and real-time collaboration features
  • 7IoT dashboards displaying real-time sensor data with user authentication and data visualization

Prerequisites

  • Docker Engine 20.10+ and Docker Compose V2 for container orchestration
  • 8GB RAM minimum for running all Supabase microservices simultaneously
  • Node.js 18+ knowledge for Next.js development and API route customization
  • PostgreSQL understanding for database schema design and row-level security policies
  • Available ports 3000 (Next.js), 8000 (Kong gateway), and 8443 (Kong HTTPS)
  • JWT secret generation capability and environment variable management experience

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 nextjs:
3 build:
4 context: .
5 dockerfile: Dockerfile
6 ports:
7 - "3000:3000"
8 environment:
9 NEXT_PUBLIC_SUPABASE_URL: http://localhost:8000
10 NEXT_PUBLIC_SUPABASE_ANON_KEY: ${SUPABASE_ANON_KEY}
11 SUPABASE_SERVICE_ROLE_KEY: ${SUPABASE_SERVICE_KEY}
12 depends_on:
13 - kong
14 networks:
15 - supabase-net
16 restart: unless-stopped
17
18 kong:
19 image: kong:latest
20 ports:
21 - "8000:8000"
22 - "8443:8443"
23 environment:
24 KONG_DATABASE: "off"
25 KONG_DECLARATIVE_CONFIG: /var/lib/kong/kong.yml
26 volumes:
27 - ./kong.yml:/var/lib/kong/kong.yml:ro
28 networks:
29 - supabase-net
30 restart: unless-stopped
31
32 auth:
33 image: supabase/gotrue:latest
34 environment:
35 GOTRUE_API_HOST: 0.0.0.0
36 GOTRUE_API_PORT: 9999
37 GOTRUE_DB_DRIVER: postgres
38 GOTRUE_DB_DATABASE_URL: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}?sslmode=disable
39 GOTRUE_SITE_URL: http://localhost:3000
40 GOTRUE_JWT_SECRET: ${JWT_SECRET}
41 depends_on:
42 db:
43 condition: service_healthy
44 networks:
45 - supabase-net
46 restart: unless-stopped
47
48 rest:
49 image: postgrest/postgrest:latest
50 environment:
51 PGRST_DB_URI: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}
52 PGRST_DB_ANON_ROLE: anon
53 PGRST_JWT_SECRET: ${JWT_SECRET}
54 depends_on:
55 db:
56 condition: service_healthy
57 networks:
58 - supabase-net
59 restart: unless-stopped
60
61 realtime:
62 image: supabase/realtime:latest
63 environment:
64 DB_HOST: db
65 DB_PORT: 5432
66 DB_USER: ${POSTGRES_USER}
67 DB_PASSWORD: ${POSTGRES_PASSWORD}
68 DB_NAME: ${POSTGRES_DB}
69 PORT: 4000
70 JWT_SECRET: ${JWT_SECRET}
71 depends_on:
72 db:
73 condition: service_healthy
74 networks:
75 - supabase-net
76 restart: unless-stopped
77
78 storage:
79 image: supabase/storage-api:latest
80 environment:
81 ANON_KEY: ${SUPABASE_ANON_KEY}
82 SERVICE_KEY: ${SUPABASE_SERVICE_KEY}
83 POSTGREST_URL: http://rest:3000
84 PGRST_JWT_SECRET: ${JWT_SECRET}
85 DATABASE_URL: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}
86 STORAGE_BACKEND: file
87 FILE_STORAGE_BACKEND_PATH: /var/lib/storage
88 volumes:
89 - storage_data:/var/lib/storage
90 depends_on:
91 db:
92 condition: service_healthy
93 rest:
94 condition: service_started
95 networks:
96 - supabase-net
97 restart: unless-stopped
98
99 db:
100 image: supabase/postgres:latest
101 environment:
102 POSTGRES_USER: ${POSTGRES_USER}
103 POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
104 POSTGRES_DB: ${POSTGRES_DB}
105 volumes:
106 - postgres_data:/var/lib/postgresql/data
107 healthcheck:
108 test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
109 interval: 10s
110 timeout: 5s
111 retries: 5
112 networks:
113 - supabase-net
114 restart: unless-stopped
115
116volumes:
117 postgres_data:
118 storage_data:
119
120networks:
121 supabase-net:
122 driver: bridge

.env Template

.env
1# PostgreSQL
2POSTGRES_USER=supabase
3POSTGRES_PASSWORD=secure_postgres_password
4POSTGRES_DB=supabase
5
6# Supabase Keys
7JWT_SECRET=your-super-secret-jwt-token-with-at-least-32-characters
8SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
9SUPABASE_SERVICE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Usage Notes

  1. 1Generate JWT keys for Supabase authentication
  2. 2Next.js app at http://localhost:3000
  3. 3Supabase API at http://localhost:8000
  4. 4Create kong.yml for API gateway configuration

Individual Services(7 services)

Copy individual services to mix and match with your existing compose files.

nextjs
nextjs:
  build:
    context: .
    dockerfile: Dockerfile
  ports:
    - "3000:3000"
  environment:
    NEXT_PUBLIC_SUPABASE_URL: http://localhost:8000
    NEXT_PUBLIC_SUPABASE_ANON_KEY: ${SUPABASE_ANON_KEY}
    SUPABASE_SERVICE_ROLE_KEY: ${SUPABASE_SERVICE_KEY}
  depends_on:
    - kong
  networks:
    - supabase-net
  restart: unless-stopped
kong
kong:
  image: kong:latest
  ports:
    - "8000:8000"
    - "8443:8443"
  environment:
    KONG_DATABASE: "off"
    KONG_DECLARATIVE_CONFIG: /var/lib/kong/kong.yml
  volumes:
    - ./kong.yml:/var/lib/kong/kong.yml:ro
  networks:
    - supabase-net
  restart: unless-stopped
auth
auth:
  image: supabase/gotrue:latest
  environment:
    GOTRUE_API_HOST: 0.0.0.0
    GOTRUE_API_PORT: 9999
    GOTRUE_DB_DRIVER: postgres
    GOTRUE_DB_DATABASE_URL: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}?sslmode=disable
    GOTRUE_SITE_URL: http://localhost:3000
    GOTRUE_JWT_SECRET: ${JWT_SECRET}
  depends_on:
    db:
      condition: service_healthy
  networks:
    - supabase-net
  restart: unless-stopped
rest
rest:
  image: postgrest/postgrest:latest
  environment:
    PGRST_DB_URI: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}
    PGRST_DB_ANON_ROLE: anon
    PGRST_JWT_SECRET: ${JWT_SECRET}
  depends_on:
    db:
      condition: service_healthy
  networks:
    - supabase-net
  restart: unless-stopped
realtime
realtime:
  image: supabase/realtime:latest
  environment:
    DB_HOST: db
    DB_PORT: 5432
    DB_USER: ${POSTGRES_USER}
    DB_PASSWORD: ${POSTGRES_PASSWORD}
    DB_NAME: ${POSTGRES_DB}
    PORT: 4000
    JWT_SECRET: ${JWT_SECRET}
  depends_on:
    db:
      condition: service_healthy
  networks:
    - supabase-net
  restart: unless-stopped
storage
storage:
  image: supabase/storage-api:latest
  environment:
    ANON_KEY: ${SUPABASE_ANON_KEY}
    SERVICE_KEY: ${SUPABASE_SERVICE_KEY}
    POSTGREST_URL: http://rest:3000
    PGRST_JWT_SECRET: ${JWT_SECRET}
    DATABASE_URL: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}
    STORAGE_BACKEND: file
    FILE_STORAGE_BACKEND_PATH: /var/lib/storage
  volumes:
    - storage_data:/var/lib/storage
  depends_on:
    db:
      condition: service_healthy
    rest:
      condition: service_started
  networks:
    - supabase-net
  restart: unless-stopped
db
db:
  image: supabase/postgres:latest
  environment:
    POSTGRES_USER: ${POSTGRES_USER}
    POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    POSTGRES_DB: ${POSTGRES_DB}
  volumes:
    - postgres_data:/var/lib/postgresql/data
  healthcheck:
    test:
      - CMD-SHELL
      - pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}
    interval: 10s
    timeout: 5s
    retries: 5
  networks:
    - supabase-net
  restart: unless-stopped

Quick Start

terminal
1# 1. Create the compose file
2cat > docker-compose.yml << 'EOF'
3services:
4 nextjs:
5 build:
6 context: .
7 dockerfile: Dockerfile
8 ports:
9 - "3000:3000"
10 environment:
11 NEXT_PUBLIC_SUPABASE_URL: http://localhost:8000
12 NEXT_PUBLIC_SUPABASE_ANON_KEY: ${SUPABASE_ANON_KEY}
13 SUPABASE_SERVICE_ROLE_KEY: ${SUPABASE_SERVICE_KEY}
14 depends_on:
15 - kong
16 networks:
17 - supabase-net
18 restart: unless-stopped
19
20 kong:
21 image: kong:latest
22 ports:
23 - "8000:8000"
24 - "8443:8443"
25 environment:
26 KONG_DATABASE: "off"
27 KONG_DECLARATIVE_CONFIG: /var/lib/kong/kong.yml
28 volumes:
29 - ./kong.yml:/var/lib/kong/kong.yml:ro
30 networks:
31 - supabase-net
32 restart: unless-stopped
33
34 auth:
35 image: supabase/gotrue:latest
36 environment:
37 GOTRUE_API_HOST: 0.0.0.0
38 GOTRUE_API_PORT: 9999
39 GOTRUE_DB_DRIVER: postgres
40 GOTRUE_DB_DATABASE_URL: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}?sslmode=disable
41 GOTRUE_SITE_URL: http://localhost:3000
42 GOTRUE_JWT_SECRET: ${JWT_SECRET}
43 depends_on:
44 db:
45 condition: service_healthy
46 networks:
47 - supabase-net
48 restart: unless-stopped
49
50 rest:
51 image: postgrest/postgrest:latest
52 environment:
53 PGRST_DB_URI: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}
54 PGRST_DB_ANON_ROLE: anon
55 PGRST_JWT_SECRET: ${JWT_SECRET}
56 depends_on:
57 db:
58 condition: service_healthy
59 networks:
60 - supabase-net
61 restart: unless-stopped
62
63 realtime:
64 image: supabase/realtime:latest
65 environment:
66 DB_HOST: db
67 DB_PORT: 5432
68 DB_USER: ${POSTGRES_USER}
69 DB_PASSWORD: ${POSTGRES_PASSWORD}
70 DB_NAME: ${POSTGRES_DB}
71 PORT: 4000
72 JWT_SECRET: ${JWT_SECRET}
73 depends_on:
74 db:
75 condition: service_healthy
76 networks:
77 - supabase-net
78 restart: unless-stopped
79
80 storage:
81 image: supabase/storage-api:latest
82 environment:
83 ANON_KEY: ${SUPABASE_ANON_KEY}
84 SERVICE_KEY: ${SUPABASE_SERVICE_KEY}
85 POSTGREST_URL: http://rest:3000
86 PGRST_JWT_SECRET: ${JWT_SECRET}
87 DATABASE_URL: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}
88 STORAGE_BACKEND: file
89 FILE_STORAGE_BACKEND_PATH: /var/lib/storage
90 volumes:
91 - storage_data:/var/lib/storage
92 depends_on:
93 db:
94 condition: service_healthy
95 rest:
96 condition: service_started
97 networks:
98 - supabase-net
99 restart: unless-stopped
100
101 db:
102 image: supabase/postgres:latest
103 environment:
104 POSTGRES_USER: ${POSTGRES_USER}
105 POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
106 POSTGRES_DB: ${POSTGRES_DB}
107 volumes:
108 - postgres_data:/var/lib/postgresql/data
109 healthcheck:
110 test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
111 interval: 10s
112 timeout: 5s
113 retries: 5
114 networks:
115 - supabase-net
116 restart: unless-stopped
117
118volumes:
119 postgres_data:
120 storage_data:
121
122networks:
123 supabase-net:
124 driver: bridge
125EOF
126
127# 2. Create the .env file
128cat > .env << 'EOF'
129# PostgreSQL
130POSTGRES_USER=supabase
131POSTGRES_PASSWORD=secure_postgres_password
132POSTGRES_DB=supabase
133
134# Supabase Keys
135JWT_SECRET=your-super-secret-jwt-token-with-at-least-32-characters
136SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
137SUPABASE_SERVICE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
138EOF
139
140# 3. Start the services
141docker compose up -d
142
143# 4. View logs
144docker 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/next-supabase-stack/run | bash

Troubleshooting

  • GoTrue authentication failing: Verify JWT_SECRET matches across auth, rest, and realtime services and ensure GOTRUE_SITE_URL points to correct Next.js origin
  • PostgREST API returning 401 errors: Check that PGRST_DB_ANON_ROLE exists in PostgreSQL and JWT_SECRET environment variable is identical across services
  • Real-time subscriptions not working: Confirm realtime service can connect to database and JWT_SECRET is properly configured for WebSocket authentication
  • Storage API uploads failing: Ensure storage service has proper file system permissions for /var/lib/storage volume and PostgREST URL is accessible
  • Kong gateway returning 502 errors: Verify all upstream Supabase services are healthy and kong.yml configuration file contains correct service definitions
  • Next.js cannot connect to Supabase: Check NEXT_PUBLIC_SUPABASE_URL points to Kong gateway (port 8000) and SUPABASE_ANON_KEY matches the generated JWT token

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

nextjssupabase-dbsupabase-authsupabase-storagesupabase-realtime

Tags

#nextjs#supabase#postgresql#realtime#auth

Category

Full Web Stacks
Ad Space