Next.js + Supabase Stack
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: Dockerfile6 ports: 7 - "3000:3000"8 environment: 9 NEXT_PUBLIC_SUPABASE_URL: http://localhost:800010 NEXT_PUBLIC_SUPABASE_ANON_KEY: ${SUPABASE_ANON_KEY}11 SUPABASE_SERVICE_ROLE_KEY: ${SUPABASE_SERVICE_KEY}12 depends_on: 13 - kong14 networks: 15 - supabase-net16 restart: unless-stopped1718 kong: 19 image: kong:latest20 ports: 21 - "8000:8000"22 - "8443:8443"23 environment: 24 KONG_DATABASE: "off"25 KONG_DECLARATIVE_CONFIG: /var/lib/kong/kong.yml26 volumes: 27 - ./kong.yml:/var/lib/kong/kong.yml:ro28 networks: 29 - supabase-net30 restart: unless-stopped3132 auth: 33 image: supabase/gotrue:latest34 environment: 35 GOTRUE_API_HOST: 0.0.0.036 GOTRUE_API_PORT: 999937 GOTRUE_DB_DRIVER: postgres38 GOTRUE_DB_DATABASE_URL: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}?sslmode=disable39 GOTRUE_SITE_URL: http://localhost:300040 GOTRUE_JWT_SECRET: ${JWT_SECRET}41 depends_on: 42 db: 43 condition: service_healthy44 networks: 45 - supabase-net46 restart: unless-stopped4748 rest: 49 image: postgrest/postgrest:latest50 environment: 51 PGRST_DB_URI: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}52 PGRST_DB_ANON_ROLE: anon53 PGRST_JWT_SECRET: ${JWT_SECRET}54 depends_on: 55 db: 56 condition: service_healthy57 networks: 58 - supabase-net59 restart: unless-stopped6061 realtime: 62 image: supabase/realtime:latest63 environment: 64 DB_HOST: db65 DB_PORT: 543266 DB_USER: ${POSTGRES_USER}67 DB_PASSWORD: ${POSTGRES_PASSWORD}68 DB_NAME: ${POSTGRES_DB}69 PORT: 400070 JWT_SECRET: ${JWT_SECRET}71 depends_on: 72 db: 73 condition: service_healthy74 networks: 75 - supabase-net76 restart: unless-stopped7778 storage: 79 image: supabase/storage-api:latest80 environment: 81 ANON_KEY: ${SUPABASE_ANON_KEY}82 SERVICE_KEY: ${SUPABASE_SERVICE_KEY}83 POSTGREST_URL: http://rest:300084 PGRST_JWT_SECRET: ${JWT_SECRET}85 DATABASE_URL: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}86 STORAGE_BACKEND: file87 FILE_STORAGE_BACKEND_PATH: /var/lib/storage88 volumes: 89 - storage_data:/var/lib/storage90 depends_on: 91 db: 92 condition: service_healthy93 rest: 94 condition: service_started95 networks: 96 - supabase-net97 restart: unless-stopped9899 db: 100 image: supabase/postgres:latest101 environment: 102 POSTGRES_USER: ${POSTGRES_USER}103 POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}104 POSTGRES_DB: ${POSTGRES_DB}105 volumes: 106 - postgres_data:/var/lib/postgresql/data107 healthcheck: 108 test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]109 interval: 10s110 timeout: 5s111 retries: 5112 networks: 113 - supabase-net114 restart: unless-stopped115116volumes: 117 postgres_data: 118 storage_data: 119120networks: 121 supabase-net: 122 driver: bridge.env Template
.env
1# PostgreSQL2POSTGRES_USER=supabase3POSTGRES_PASSWORD=secure_postgres_password4POSTGRES_DB=supabase56# Supabase Keys7JWT_SECRET=your-super-secret-jwt-token-with-at-least-32-characters8SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...9SUPABASE_SERVICE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...Usage Notes
- 1Generate JWT keys for Supabase authentication
- 2Next.js app at http://localhost:3000
- 3Supabase API at http://localhost:8000
- 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 file2cat > docker-compose.yml << 'EOF'3services:4 nextjs:5 build:6 context: .7 dockerfile: Dockerfile8 ports:9 - "3000:3000"10 environment:11 NEXT_PUBLIC_SUPABASE_URL: http://localhost:800012 NEXT_PUBLIC_SUPABASE_ANON_KEY: ${SUPABASE_ANON_KEY}13 SUPABASE_SERVICE_ROLE_KEY: ${SUPABASE_SERVICE_KEY}14 depends_on:15 - kong16 networks:17 - supabase-net18 restart: unless-stopped1920 kong:21 image: kong:latest22 ports:23 - "8000:8000"24 - "8443:8443"25 environment:26 KONG_DATABASE: "off"27 KONG_DECLARATIVE_CONFIG: /var/lib/kong/kong.yml28 volumes:29 - ./kong.yml:/var/lib/kong/kong.yml:ro30 networks:31 - supabase-net32 restart: unless-stopped3334 auth:35 image: supabase/gotrue:latest36 environment:37 GOTRUE_API_HOST: 0.0.0.038 GOTRUE_API_PORT: 999939 GOTRUE_DB_DRIVER: postgres40 GOTRUE_DB_DATABASE_URL: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}?sslmode=disable41 GOTRUE_SITE_URL: http://localhost:300042 GOTRUE_JWT_SECRET: ${JWT_SECRET}43 depends_on:44 db:45 condition: service_healthy46 networks:47 - supabase-net48 restart: unless-stopped4950 rest:51 image: postgrest/postgrest:latest52 environment:53 PGRST_DB_URI: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}54 PGRST_DB_ANON_ROLE: anon55 PGRST_JWT_SECRET: ${JWT_SECRET}56 depends_on:57 db:58 condition: service_healthy59 networks:60 - supabase-net61 restart: unless-stopped6263 realtime:64 image: supabase/realtime:latest65 environment:66 DB_HOST: db67 DB_PORT: 543268 DB_USER: ${POSTGRES_USER}69 DB_PASSWORD: ${POSTGRES_PASSWORD}70 DB_NAME: ${POSTGRES_DB}71 PORT: 400072 JWT_SECRET: ${JWT_SECRET}73 depends_on:74 db:75 condition: service_healthy76 networks:77 - supabase-net78 restart: unless-stopped7980 storage:81 image: supabase/storage-api:latest82 environment:83 ANON_KEY: ${SUPABASE_ANON_KEY}84 SERVICE_KEY: ${SUPABASE_SERVICE_KEY}85 POSTGREST_URL: http://rest:300086 PGRST_JWT_SECRET: ${JWT_SECRET}87 DATABASE_URL: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}88 STORAGE_BACKEND: file89 FILE_STORAGE_BACKEND_PATH: /var/lib/storage90 volumes:91 - storage_data:/var/lib/storage92 depends_on:93 db:94 condition: service_healthy95 rest:96 condition: service_started97 networks:98 - supabase-net99 restart: unless-stopped100101 db:102 image: supabase/postgres:latest103 environment:104 POSTGRES_USER: ${POSTGRES_USER}105 POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}106 POSTGRES_DB: ${POSTGRES_DB}107 volumes:108 - postgres_data:/var/lib/postgresql/data109 healthcheck:110 test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]111 interval: 10s112 timeout: 5s113 retries: 5114 networks:115 - supabase-net116 restart: unless-stopped117118volumes:119 postgres_data:120 storage_data:121122networks:123 supabase-net:124 driver: bridge125EOF126127# 2. Create the .env file128cat > .env << 'EOF'129# PostgreSQL130POSTGRES_USER=supabase131POSTGRES_PASSWORD=secure_postgres_password132POSTGRES_DB=supabase133134# Supabase Keys135JWT_SECRET=your-super-secret-jwt-token-with-at-least-32-characters136SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...137SUPABASE_SERVICE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...138EOF139140# 3. Start the services141docker compose up -d142143# 4. View logs144docker 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/next-supabase-stack/run | bashTroubleshooting
- 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 StacksAd Space
Shortcuts: C CopyF FavoriteD Download