What is Supabase?
Supabase is an open source backend platform built on PostgreSQL. One stack gives you a Postgres database, auto-generated REST and GraphQL APIs, authentication, file storage, realtime subscriptions, edge functions and a web dashboard called Studio. It is the best-known open source alternative to Firebase and is licensed under Apache-2.0.
Be clear about the size before you start: self-hosted Supabase is not one container. The official stack runs around ten services (Studio, an Envoy API gateway, Auth, PostgREST, Realtime, Storage, imgproxy, postgres-meta, Edge Runtime, Supavisor and Postgres itself), and you are responsible for securing, backing up and upgrading all of it.
Requirements
- From the official docs: at least 4 GB of RAM, 2 CPU cores and a 40 GB SSD. The recommendation is 8 GB or more, 4 cores and 80 GB or more.
- Docker Engine, Docker Compose v2,
gitandopenssl. - A domain with an A record for the server, for HTTPS.
Step 1: Prepare the server
This guide assumes Ubuntu 24.04 with Docker installed from the official Docker Engine guide. Supabase versions its self-hosting files with self-hosted/vX.Y.Z tags; pin one rather than tracking master:
git clone --depth 1 --branch self-hosted/v0.8.2 https://github.com/supabase/supabase
mkdir supabase-project
cp -rf supabase/docker/. supabase-project
cd supabase-project
cp .env.example .env
printf 'ref=self-hosted/v0.8.2\n' > .supabase-version
Supabase also offers a one-line installer (setup.sh) that does the same thing, but the manual route shows you exactly what lands on the server.
Step 2: Create the Docker Compose file
You do not write this Compose file by hand. The cloned docker-compose.yml is the official stack, with every image pinned to a tested version, and .env drives all of it. The important part is replacing every default secret before the first start. The example file warns that all of them must change, and the repo ships scripts that generate them:
sh utils/generate-keys.sh --update-env
sh utils/add-new-auth-keys.sh --update-env
The first script writes POSTGRES_PASSWORD, JWT_SECRET, the legacy ANON_KEY and SERVICE_ROLE_KEY, SECRET_KEY_BASE, VAULT_ENC_KEY, REALTIME_DB_ENC_KEY, PG_META_CRYPTO_KEY, DASHBOARD_PASSWORD and the Logflare and storage keys. The second derives the newer SUPABASE_PUBLISHABLE_KEY and SUPABASE_SECRET_KEY from the JWT secret. If you would rather set a value yourself, use openssl rand -hex 32 for 64-character secrets.
Then edit .env and set the public URLs:
SUPABASE_PUBLIC_URL=https://supabase.example.com
API_EXTERNAL_URL=https://supabase.example.com
SITE_URL=https://app.example.com
DASHBOARD_USERNAME=CHANGE_ME
SITE_URL is where your front end runs; Auth uses it for email links. Treat SUPABASE_SECRET_KEY and SERVICE_ROLE_KEY like database root passwords: they bypass row-level security and must never ship in client code.
Step 3: Start and open the app
docker compose pull
docker compose up -d
docker compose ps
The first pull is several gigabytes. Wait until every service reports healthy, then open http://YOUR_SERVER_IP:8000 and sign in with DASHBOARD_USERNAME and DASHBOARD_PASSWORD. Run sh run.sh secrets to see them again. In Studio, create a table, add a row-level security policy and copy the API URL and publishable key into your app.
Step 4: Put it behind HTTPS
The repo includes an official Caddy overlay that terminates TLS for your domain, removes the gateway's public port 8000 and protects Studio with the dashboard credentials. Set these in .env:
COMPOSE_FILE=docker-compose.yml:docker-compose.caddy.yml
PROXY_DOMAIN=supabase.example.com
Then run docker compose up -d. Caddy fetches a Let's Encrypt certificate and routes /auth/v1, /rest/v1, /storage/v1, /realtime/v1 and /functions/v1 to the API and everything else to Studio.
Supavisor still publishes Postgres on 5432 and 6543 on all interfaces. Docker-published ports bypass ufw, so block them in your cloud provider's firewall, or only allow your own IPs, unless your apps connect to the database directly from outside the server.
Backups and upgrades
Database dumps are the core of your backup:
docker compose exec -T db pg_dumpall -U supabase_admin > supabase-$(date +%F).sql
Also back up .env and volumes/storage (uploaded files). Without .env your JWT secret and encryption keys are gone, and existing tokens and encrypted values become unusable.
To update, read the self-hosted changelog, then run the bundled script, which merges new vendor files and adds missing .env keys without overwriting your values:
sh update.sh
Troubleshooting
- A service keeps restarting: run
docker compose logs <service>. Most first-start failures are placeholder secrets or a.envvalue with the wrong length. - "Invalid API key" from the client libraries: the keys were regenerated after the containers started. Run
docker compose up -d --force-recreate. - Auth emails link to localhost:
SITE_URLandAPI_EXTERNAL_URLstill have their defaults. - Server runs out of memory: stop optional services you do not use, or move to the recommended 8 GB.
Next steps
Set SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASS and SMTP_ADMIN_EMAIL in .env so Auth can send real emails, switch storage to S3 with the S3 overlay, schedule nightly pg_dumpall runs with off-site copies, and enable only the auth providers your app needs.
Spotted something out of date? Tell us and we will update the guide.