Self-Hosting Guide: Deploy Supabase Locally
·OSSAlt Team
supabasebackendself-hostingdockerguide
Self-Hosting Guide: Deploy Supabase Locally
Supabase is the open source Firebase alternative — PostgreSQL database, authentication, storage, edge functions, and real-time subscriptions. Self-hosting removes the 500 MB database limit, gives you unlimited API requests, and costs a fraction of Supabase Cloud's $25/month Pro plan.
Requirements
- VPS with 4 GB RAM minimum (8 GB recommended)
- Docker and Docker Compose
- Domain name (e.g.,
supabase.yourdomain.com) - 30+ GB disk
Step 1: Clone Supabase Docker
git clone --depth 1 https://github.com/supabase/supabase.git
cd supabase/docker
# Copy environment
cp .env.example .env
Step 2: Generate Secrets
# Generate JWT secret
openssl rand -hex 32
# Generate anon key and service role key using the JWT secret
# Use https://supabase.com/docs/guides/self-hosting/docker#generate-api-keys
# Or generate manually with jwt.io
Step 3: Configure Environment
Edit .env:
# General
SITE_URL=https://supabase.yourdomain.com
API_EXTERNAL_URL=https://supabase.yourdomain.com
# JWT
JWT_SECRET=your-jwt-secret-min-32-chars
ANON_KEY=your-generated-anon-key
SERVICE_ROLE_KEY=your-generated-service-role-key
# Database
POSTGRES_PASSWORD=your-strong-password
POSTGRES_HOST=db
POSTGRES_DB=postgres
POSTGRES_PORT=5432
# Dashboard
DASHBOARD_USERNAME=admin
DASHBOARD_PASSWORD=your-dashboard-password
# SMTP
SMTP_HOST=smtp.resend.com
SMTP_PORT=587
SMTP_USER=resend
SMTP_PASS=re_your_api_key
SMTP_SENDER_NAME=Supabase
SMTP_ADMIN_EMAIL=admin@yourdomain.com
# Storage
STORAGE_BACKEND=file
FILE_SIZE_LIMIT=52428800
Step 4: Start Supabase
docker compose up -d
This starts all Supabase services:
| Service | Port | Purpose |
|---|---|---|
| Kong (API Gateway) | 8000 | API routing |
| GoTrue | 9999 | Authentication |
| PostgREST | 3000 | REST API from PostgreSQL |
| Realtime | 4000 | WebSocket subscriptions |
| Storage | 5000 | File storage API |
| Studio | 3001 | Dashboard UI |
| PostgreSQL | 5432 | Database |
| Meta | 8080 | Metadata API |
Step 5: Reverse Proxy (Caddy)
# /etc/caddy/Caddyfile
supabase.yourdomain.com {
# API Gateway
reverse_proxy localhost:8000
}
studio.yourdomain.com {
# Dashboard
reverse_proxy localhost:3001
}
sudo systemctl restart caddy
Step 6: Access Dashboard
- Open
https://studio.yourdomain.com - Login with your dashboard credentials
- Create your first project
Step 7: Connect Your App
import { createClient } from '@supabase/supabase-js'
const supabase = createClient(
'https://supabase.yourdomain.com',
'your-anon-key'
)
// Query data
const { data, error } = await supabase
.from('todos')
.select('*')
// Insert data
const { data, error } = await supabase
.from('todos')
.insert({ title: 'My todo', completed: false })
// Auth
const { data, error } = await supabase.auth.signUp({
email: 'user@example.com',
password: 'password123',
})
// Storage
const { data, error } = await supabase.storage
.from('avatars')
.upload('user1/avatar.png', file)
// Realtime
supabase
.channel('todos')
.on('postgres_changes', { event: '*', schema: 'public', table: 'todos' },
(payload) => console.log('Change:', payload)
)
.subscribe()
Step 8: Set Up Row Level Security
Enable RLS on your tables for production security:
-- Enable RLS
ALTER TABLE todos ENABLE ROW LEVEL SECURITY;
-- Users can only read their own todos
CREATE POLICY "Users read own todos"
ON todos FOR SELECT
USING (auth.uid() = user_id);
-- Users can only insert their own todos
CREATE POLICY "Users insert own todos"
ON todos FOR INSERT
WITH CHECK (auth.uid() = user_id);
-- Users can only update their own todos
CREATE POLICY "Users update own todos"
ON todos FOR UPDATE
USING (auth.uid() = user_id);
Production Hardening
Restrict Studio access:
- Put Studio behind VPN or IP allowlist
- Use strong dashboard credentials
- Consider disabling Studio in production
Backups:
# Database backup (daily cron)
docker exec supabase-db pg_dump -U postgres postgres > /backups/supabase-$(date +%Y%m%d).sql
# Storage backup
tar czf /backups/supabase-storage-$(date +%Y%m%d).tar.gz ./volumes/storage
Updates:
cd supabase/docker
git pull
docker compose pull
docker compose up -d
Monitoring:
- Monitor Kong API gateway (port 8000)
- Monitor PostgreSQL connections and query performance
- Set up disk space alerts (database grows)
- Monitor GoTrue for auth failures
Resource Usage
| Scale | RAM | CPU | Disk |
|---|---|---|---|
| Development | 4 GB | 2 cores | 20 GB |
| Small app (1K users) | 8 GB | 4 cores | 50 GB |
| Medium app (10K users) | 16 GB | 8 cores | 100 GB |
VPS Recommendations
| Provider | Spec (small app) | Price |
|---|---|---|
| Hetzner | 4 vCPU, 8 GB RAM | €8/month |
| DigitalOcean | 4 vCPU, 8 GB RAM | $48/month |
| Linode | 4 vCPU, 8 GB RAM | $48/month |
Supabase is resource-intensive due to multiple services. Use Hetzner for the best price-performance ratio.
Compare backend platforms on OSSAlt — features, pricing, and self-hosting options side by side.