Skip to content

SUPABASE ARCHITECTURE

This document explains how the local Supabase services work together.

┌─────────────────────────────────────────────────────────────┐
│ Your Application │
│ (React App - localhost:5173) │
│ │
│ VITE_SUPABASE_URL=http://localhost:54321 │
│ VITE_SUPABASE_ANON_KEY=eyJ... │
└──────────────────────┬───────────────────────────────────────┘
│ HTTP/WebSocket
┌──────────────────────▼───────────────────────────────────────┐
│ Kong API Gateway (54321) │
│ │
│ • Routes requests to correct service │
│ • Validates JWT tokens │
│ • Enforces CORS policies │
│ • Rate limiting & auth middleware │
└──┬────────┬────────┬────────┬───────────────────────────────┘
│ │ │ │
│ /rest │ /auth │ /realtime │ /storage
│ │ │ │
┌──▼───┐ ┌─▼────┐ ┌─▼──────┐ ┌──▼─────┐
│PostgR│ │GoTrue│ │Realtime│ │Storage │
│ EST │ │ │ │ │ │ API │
│ │ │ │ │ │ │ │
│REST │ │Auth │ │WebSock │ │Objects │
│API │ │Mgmt │ │Server │ │Files │
└──┬───┘ └─┬────┘ └──┬─────┘ └──┬─────┘
│ │ │ │
└───────┴─────────┴──────────┘
┌─────▼──────────────────────────────────────┐
│ PostgreSQL Database │
│ (localhost:5432) │
│ │
│ • Main data storage │
│ • Extensions: pgvector, postgis, etc. │
│ • Schemas: public, auth, storage, etc. │
│ • Row Level Security (RLS) enabled │
└─────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Supabase Studio (54323) │
│ │
│ Web UI for: │
│ • Database schema management │
│ • Table editor & SQL editor │
│ • Auth user management │
│ • Storage bucket management │
│ • API documentation │
└─────────────────────────────────────────────────────────────┘
Your App
│ GET /rest/v1/users
│ Authorization: Bearer <JWT>
Kong Gateway
│ • Validates JWT
│ • Checks role (anon/authenticated/service_role)
│ • Routes to PostgREST
PostgREST
│ • Connects to database as appropriate role
│ • Executes SQL query
│ • Applies RLS policies
│ • Returns JSON
PostgreSQL
│ • Checks RLS policies
│ • Executes query
│ • Returns results
Response flows back through Kong to Your App
Your App
│ POST /auth/v1/signup
│ { email, password }
Kong Gateway
│ • Routes to GoTrue
GoTrue
│ • Validates input
│ • Hashes password
│ • Creates user in auth.users
│ • Sends confirmation email (auto-confirmed in dev)
│ • Generates JWT
PostgreSQL (auth schema)
│ • Stores user credentials
│ • Triggers user_profiles creation
Response with JWT
│ { access_token, refresh_token, user }
Your App stores tokens
Your App
│ WebSocket: /realtime/v1/websocket
│ Subscribe to: public.messages
Kong Gateway
│ • Validates JWT
│ • Upgrades to WebSocket
│ • Routes to Realtime server
Realtime Server
│ • Establishes WebSocket connection
│ • Subscribes to PostgreSQL changes
│ • Filters by RLS policies
PostgreSQL (logical replication)
│ • Streams changes via WAL
│ • Applies RLS filters
Changes stream to Your App via WebSocket
Your App
│ POST /storage/v1/object/avatars/user.jpg
│ Authorization: Bearer <JWT>
│ Content-Type: image/jpeg
Kong Gateway
│ • Validates JWT
│ • Routes to Storage API
Storage API
│ • Checks bucket policies
│ • Validates file type/size
│ • Saves to filesystem
│ • Creates database record
Filesystem (docker/volumes/storage/)
│ • File stored at path
PostgreSQL (storage schema)
│ • Metadata stored
│ • RLS applied
Response with file URL
postgres (superuser)
├─ supabase_admin (admin operations)
├─ authenticator (connection pooler)
│ │
│ ├─ anon (unauthenticated users)
│ │ └─ SELECT public tables (if allowed by RLS)
│ │
│ ├─ authenticated (logged-in users)
│ │ └─ SELECT/INSERT/UPDATE/DELETE (per RLS)
│ │
│ └─ service_role (backend/admin)
│ └─ BYPASS RLS (full access)
├─ supabase_auth_admin (GoTrue)
│ └─ Full access to auth schema
└─ supabase_storage_admin (Storage)
└─ Full access to storage schema
supabase-network (Docker bridge network)
├─ db:5432 (PostgreSQL)
├─ rest:3000 (PostgREST)
├─ auth:9999 (GoTrue)
├─ realtime:4000 (Realtime)
├─ storage:5000 (Storage)
├─ kong:8000 (Kong internal)
├─ studio:3000 (Studio)
├─ meta:8080 (PostgreSQL Meta)
├─ analytics:4000 (Logflare)
├─ imgproxy:5001 (Image Proxy)
└─ vector:9001 (Log Router)
All services communicate using service names (e.g., http://db:5432)
localhost:54321 → Kong → All API services
localhost:54323 → Studio UI
localhost:5432 → PostgreSQL (direct connection)
localhost:54324 → Analytics dashboard
1. Client sends INSERT request
2. Kong validates JWT and routes to PostgREST
3. PostgREST connects as appropriate role
4. PostgreSQL applies RLS policies
5. Row inserted if policies pass
6. PostgreSQL triggers fire (if any)
7. Change logged to Write-Ahead Log (WAL)
8. Realtime server reads WAL
9. Realtime broadcasts to subscribed clients
10. Response sent back to original client
1. Client sends SELECT request
2. Kong validates JWT and routes to PostgREST
3. PostgREST connects as appropriate role
4. PostgreSQL applies RLS policies
5. Only visible rows returned
6. Response formatted as JSON
7. Sent back through Kong to client
  • Docker internal network isolation
  • Only necessary ports exposed
  • Kong acts as single entry point
  • JWT validation on every request
  • Token expiry (default 1 hour)
  • Refresh token rotation
  • Role-based access (anon/authenticated/service_role)
  • Row Level Security (RLS) policies
  • Column-level permissions
  • Encrypted connections (in production)
  • Password hashing (bcrypt)
  • SQL injection prevention (parameterized queries)
Configures:
- Database credentials
- JWT secrets
- API endpoints
- Service ports
- Email settings
Defines:
- Service routes
- Authentication rules
- CORS policies
- Rate limits
Sets up:
- Database roles
- Schemas (_realtime, _analytics, etc.)
- Permissions
- Extensions

Each service has health checks:

PostgreSQL: pg_isready
PostgREST: Health endpoint
GoTrue: /health endpoint
Realtime: /health endpoint
Storage: /status endpoint
Kong: Admin API
Studio: HTTP request
Analytics: /health endpoint
ImgProxy: health command
Vector: /health endpoint

View status: make supabase-status

Vector (Log Router)
│ Collects logs from all containers
Logflare (Analytics)
│ Aggregates and stores logs
Studio Dashboard
│ Displays logs in UI

Access logs: make supabase-logs

Database → pg_dump → backups/local-backup-YYYYMMDD.sql
backups/backup.sql → psql → PostgreSQL

Commands:

  • make supabase-backup - Create backup
  • make supabase-restore FILE=backup.sql - Restore
AspectLocalProduction
NetworkDocker internalInternet
HTTPSNo (HTTP only)Yes (required)
EmailsAuto-confirmedReal delivery
StorageLocal filesS3/GCS
ScalingSingle nodeDistributed
BackupsManualAutomated
MonitoringBasic logsFull observability
Terminal window
make supabase-status
docker-compose ps
Terminal window
make supabase-logs
docker-compose logs <service>
Terminal window
# Database
make supabase-db
# API
curl http://localhost:54321/rest/v1/
# Studio
open http://localhost:54323
Terminal window
make supabase-reset
make supabase-start
make supabase-migrate
  • Max connections: 100 (default)
  • Pooling via authenticator role
  • Connection timeout: 30s
  • Default: Unlimited in local
  • Configure in kong.yml if needed
  • Adjust in docker-compose.yml
  • Default: Docker desktop limits
  1. Use RLS Everywhere: Always enable Row Level Security
  2. Test Migrations Locally: Before production deploy
  3. Backup Regularly: Before major changes
  4. Monitor Logs: Keep make supabase-logs running
  5. Reset Often: Test fresh installation
  6. Version Lock: Pin service versions in docker-compose.yml
  7. Secure Secrets: Never commit .env.local to git