Supabase is an open-source Firebase alternative built on PostgreSQL: a Postgres database with auto-generated REST and GraphQL APIs, authentication, row-level security, realtime subscriptions, file storage and edge functions, managed through the Studio dashboard. The hosted platform is popular, and the same stack can be self-hosted with Docker.
Lighter alternatives: PocketBase (a single binary) and Appwrite.
What you get (the containers)
| Service | Role |
|---|---|
| PostgreSQL | The database (with Supabase extensions) |
| Kong | API gateway, the single entry point |
| GoTrue (auth) | Users, sessions, OAuth, magic links |
| PostgREST | Auto-generated REST API from your schema |
| Realtime | Changes over WebSockets |
| Storage + imgproxy | Files with access policies, image transforms |
| Studio | Admin dashboard |
| Edge runtime | Deno functions |
| Supavisor / Meta / Analytics | Pooling, metadata API, logs |
Requirements
- 4 GB RAM minimum (8 GB recommended), 2+ vCPU and SSD storage.
- Docker and Docker Compose. See install Docker.
- A domain with HTTPS (a reverse proxy in front of Kong).
Setup
git clone --depth 1 https://github.com/supabase/supabase /opt/supabase-src
mkdir -p /opt/supabase && cp -r /opt/supabase-src/docker/* /opt/supabase/
cp /opt/supabase-src/docker/.env.example /opt/supabase/.env
cd /opt/supabase
Generate secrets, before the first start
Edit .env and set at least:
POSTGRES_PASSWORD: a strong password;JWT_SECRET: at least 32 random characters;ANON_KEYandSERVICE_ROLE_KEY: JWTs signed with yourJWT_SECRET(the Supabase self-hosting docs include a generator). Don’t keep the example keys;DASHBOARD_USERNAME/DASHBOARD_PASSWORD: protects Studio through Kong basic auth;SITE_URL(your app’s URL) andAPI_EXTERNAL_URL/SUPABASE_PUBLIC_URL(e.g.https://supabase.example.com);- SMTP settings (
SMTP_HOST,SMTP_PORT,SMTP_USER,SMTP_PASS,SMTP_ADMIN_EMAIL) for auth emails. See transactional email services.
docker compose pull
docker compose up -d
docker compose ps
Never expose the SERVICE_ROLE_KEY to browsers. It bypasses row-level security. Use it only on servers. See secrets management.
Reverse proxy and HTTPS
Kong listens on port 8000. Bind it to localhost (edit the compose port mapping), and put Nginx in front:
server {
server_name supabase.example.com;
client_max_body_size 100M;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
# listen 443 ssl; certbot --nginx -d supabase.example.com
}
Restrict Studio further (VPN or an IP allow-list) if possible. See Nginx WebSocket proxy and zero trust networking.
Don’t publish PostgreSQL’s port (5432) or the pooler to the internet unless needed, and then with strong passwords, TLS and a firewall allow-list.
Row-level security: the key concept
Supabase clients query Postgres directly through PostgREST using the anon key, so security lives in Postgres policies:
alter table notes enable row level security;
create policy "own notes" on notes
for all using (auth.uid() = user_id) with check (auth.uid() = user_id);
Every table exposed to clients needs RLS enabled, plus appropriate policies. Studio warns you about tables without RLS.
Backups
Self-hosting means you handle backups (the cloud has managed ones):
docker compose exec -T db pg_dumpall -U postgres | gzip > /backups/supabase-$(date +%F).sql.gz
Also back up the storage volume (files) and .env. For point-in-time recovery, use WAL archiving with pgBackRest or WAL-G. See pgBackRest and backup strategy.
Self-hosted vs the cloud
| Self-hosted | Supabase Cloud | |
|---|---|---|
| Cost | Server only | Free tier, then usage-based |
| Backups, PITR, upgrades | You | Managed |
| Branching, some dashboard features | Limited | Full |
| Data location and control | Full | Provider regions |
Updates
Pull the latest docker/ folder from the repository, compare it with your docker-compose.yml and .env, then docker compose pull && docker compose up -d. Major Postgres version changes need dump and restore or pg_upgrade. See updating self-hosted apps.
Quick reference
| Item | Value |
|---|---|
| Gateway (Kong) | port 8000 |
| Studio login | DASHBOARD_USERNAME / DASHBOARD_PASSWORD |
| Client key | ANON_KEY (safe in browsers with RLS) |
| Server key | SERVICE_ROLE_KEY (never in browsers) |
| Backup | pg_dumpall + storage volume + .env |
Related: PostgreSQL monitoring and JWT explained.