Supabase is the open-source Firebase alternative, and it's exceptional — Postgres at the core, plus auth, auto-generated REST and GraphQL APIs, realtime, storage, and edge functions. Self-hosting Supabase is absolutely doable, but it's important to understand up front: Supabase is not one service. It's a curated stack of around a dozen services orchestrated around a single PostgreSQL database. This guide demystifies that stack and covers realistic self-hosting paths.
What's actually in the box
Supabase is best understood as PostgreSQL plus a set of services that expose it:
| Service | Role |
|---|---|
| PostgreSQL | The heart — your data, with extensions |
| GoTrue (Auth) | Authentication and user management |
| PostgREST | Auto-generated REST API from your schema |
| Realtime | Postgres change streams over WebSockets |
| Storage API | S3-backed file storage with row-level security |
| Kong | API gateway routing all of the above |
| Studio | The admin dashboard |
| pg-meta | Database introspection for Studio |
| Edge Functions (Deno) | Serverless functions |
The genius is that Postgres is the single source of truth — auth, row-level security, and the APIs all derive from your database schema. The cost is operational: that's many services to run and keep in sync.
Step 1: Start from the official Compose stack
Supabase publishes an official self-hosting setup based on Docker Compose. Clone it and configure:
git clone --depth 1 https://github.com/supabase/supabase
cd supabase/docker
cp .env.example .envThe .env file holds the secrets that make or break your deployment:
POSTGRES_PASSWORD=<strong password>
JWT_SECRET=<40+ char random string>
ANON_KEY=<JWT signed with JWT_SECRET>
SERVICE_ROLE_KEY=<JWT signed with JWT_SECRET>
DASHBOARD_USERNAME=admin
DASHBOARD_PASSWORD=<strong password>Critical: ANON_KEY and SERVICE_ROLE_KEY are JWTs signed with JWT_SECRET. They must be generated *from* your JWT_SECRET (Supabase provides a generator). Mismatched keys mean nothing authenticates. And the SERVICE_ROLE_KEY bypasses row-level security — never expose it to clients.
Step 2: What must persist
The stateful pieces:
- PostgreSQL data — everything. This is the one you absolutely cannot lose.
- Storage volume — uploaded files (unless you back Storage with external S3/MinIO).
Everything else (Kong, PostgREST, GoTrue, Realtime) is stateless and derives from Postgres.
Step 3: Realistic self-hosting patterns
Pattern A — Full Compose stack on a compute instance. Run the entire official Compose stack on one compute instance with a persistent volume for Postgres and storage. This is the most faithful reproduction of Supabase and keeps all services on one Docker network. It's the simplest to stand up and the easiest to reason about. The downside is everything shares one box.
Pattern B — Managed Postgres + Supabase services. This is the more cloud-native split: use a managed PostgreSQL for the database (you'll need the Supabase-required extensions and roles), and run the stateless Supabase services (GoTrue, PostgREST, Realtime, Storage, Kong, Studio) as container apps pointed at that managed database. This gives you a managed, backed-up Postgres while keeping the API layer scalable and stateless.
A note of honesty on Pattern B: Supabase's self-hosted images expect specific Postgres roles (authenticator, supabase_admin, etc.) and extensions. Using an external managed Postgres requires bootstrapping those roles and extensions, which is more involved than pointing at the bundled Postgres image. The official docs cover the required roles. If your managed Postgres allows the needed extensions and superuser-ish setup, Pattern B works well; if it's locked down, Pattern A is safer.
Step 4: Deploy on PandaStack
For Pattern B on PandaStack:
- 1Provision a managed PostgreSQL (16.x). Bootstrap the Supabase roles and extensions per the docs.
- 2Deploy the stateless services as container apps using Supabase's official images, each configured via env vars to reach your Postgres and share
JWT_SECRET. - 3Put Kong in front as the gateway and attach a custom domain with automatic SSL.
- 4Back the Storage service with a MinIO or S3 bucket so files don't depend on a local volume.
- 5Deploy Studio behind authentication for admin access.
For Pattern A, run the Compose stack on a single compute instance with persistent volumes and point a domain at Kong.
Step 5: Connect your app
Your client app uses the anon key and your domain:
import { createClient } from '@supabase/supabase-js';
const supabase = createClient(
'https://supabase.yourdomain.com',
process.env.SUPABASE_ANON_KEY // the anon JWT, safe for browsers
);Row-level security policies in Postgres are what keep the anon key safe to ship — enable RLS on every table.
Honest caveats
Self-hosting Supabase is the heaviest lift in this series. You're operating a dozen services and a database, keeping image versions compatible, and managing JWT key derivation correctly. The Supabase team is candid that the hosted platform gets features first and that self-hosting is a more manual experience. If you simply want Postgres with auto APIs and don't need the whole stack, you can cherry-pick (e.g., just Postgres + PostgREST). And if you want Firebase-like simplicity without this operational weight, PocketBase or the hosted Supabase tier may serve you better. That said, for teams who want the full Supabase developer experience on their own infrastructure, it's genuinely achievable — just plan for the ops.
Wrapping up
Supabase self-hosting comes down to understanding that everything orbits one PostgreSQL database. Get your JWT_SECRET and derived keys right, persist Postgres and storage, and decide between running the full Compose stack (Pattern A) or splitting managed Postgres from stateless services (Pattern B). Either way, RLS is your security backbone.
PandaStack's managed PostgreSQL, container apps, and MinIO-friendly storage make Pattern B viable, and you can prototype the stack on the free tier before committing. Start at https://dashboard.pandastack.io.
References
- Supabase self-hosting docs: https://supabase.com/docs/guides/self-hosting
- Supabase Docker self-hosting: https://supabase.com/docs/guides/self-hosting/docker
- Supabase architecture overview: https://supabase.com/docs/guides/getting-started/architecture
- PostgREST documentation: https://postgrest.org/en/stable/
- PostgreSQL row-level security: https://www.postgresql.org/docs/current/ddl-rowsecurity.html