Skip to content

How to Deploy Supabase on a VPS

Updated Aug 2026

Self-host Supabase — the open-source Firebase alternative bundling Postgres, auth, realtime, storage, and edge functions behind one API gateway — on your own VPS with Docker Compose.

Before you start
  • A VPS with at least 4 GB RAM — Supabase is a dozen-container stack, and Postgres, Realtime, and Studio all want memory
  • A fresh Ubuntu server with root/sudo SSH access, Docker + Compose installed
  • openssl on the server (present on stock Ubuntu) — the secret-generation step uses it
  • A domain pointed at the server if you plan to expose the API or Studio over HTTPS — strongly recommended before anything real touches this box

What self-hosted Supabase actually is

Supabase is not one application — it's a coordinated stack of services behind a single API gateway, and self-hosting it means running all of them yourself via one Docker Compose file: Postgres (the actual database), PostgREST (the auto-generated REST API), GoTrue (auth — the compose service is just called auth), Realtime (database change broadcasts over websockets), Storage (file uploads, with imgproxy for image transforms), Studio (the dashboard), postgres-meta (the database-management API Studio uses), an Edge Functions runtime, Supavisor (connection pooling), and the API gateway itself that fronts everything on one port. That's roughly a dozen containers for one "app" — worth knowing before you commit.

Should you self-host Supabase?

Honest answer: often no. Supabase's hosted free tier covers a lot of hobby projects, and the thing you give up by self-hosting is substantial — on your own box there is no branching, no managed backups or point-in-time recovery, no advanced metrics beyond logs, no ETL, and no platform management API, and the stack runs as a single project (no organizations, no multi-project dashboard). Upgrades, security patching, disaster recovery, and uptime are entirely yours.

Self-hosting earns its keep when the data must stay on your infrastructure (compliance, region, policy), when you're past the free tier and a single VPS beats usage-based pricing, or when you want Postgres you can actually reach as a superuser. If none of those apply, the hosted platform is the pragmatic choice.

Server sizing

Treat 4 GB RAM as the floor — the minimum for the full stack to boot and stay up, not a comfortable number. Postgres, Realtime (an Elixir service), and Studio are each individually hungry, and a dozen containers add up. 8 GB is the sensible starting point for real traffic, and give the disk room to grow — the database and uploaded files both live on this box now. See Best VPS for self-hosting for the ranked picks.

Install Supabase

This guide assumes a box prepared per Docker & Compose on Ubuntu. The setup is a sequence of shell commands rather than a compose file you write yourself — the project ships the compose file and you copy it out of the repository. Clone it (shallow — you only need the current state) and copy the docker/ directory into a clean project folder, including the environment template:

git clone --depth 1 https://github.com/supabase/supabase
mkdir supabase-project && cp -rf supabase/docker/* supabase-project && cp supabase/docker/.env.example supabase-project/.env
cd supabase-project

Stop here. Do not run docker compose up yet — the .env you just copied is full of published placeholder secrets, and the next section replaces them first.

Secrets before first boot — not optional

The .env.example file says it in capitals: you must change all the default values before starting the containers for the first time. The defaults are not weak passwords — they are public knowledge, printed verbatim in the repository and its documentation, including working sample JWTs. A Supabase stack booted with default keys and reachable from the internet is an open database with a friendly dashboard.

The variables that must change, exactly as named in .env:

  • POSTGRES_PASSWORD — the database superuser password.
  • JWT_SECRET — signs every auth token; anyone holding it can mint valid tokens.
  • ANON_KEY and SERVICE_ROLE_KEY — the client-side and server-side API keys. These are JWTs signed by JWT_SECRET, so they must be regenerated together with it — you can't change one and keep the others.
  • DASHBOARD_USERNAME and DASHBOARD_PASSWORD — Studio sits behind HTTP basic auth using these; the defaults are supabase / a published placeholder.
  • SECRET_KEY_BASE and VAULT_ENC_KEY — internal signing and encryption keys with published defaults.

The repository ships a generator that handles the fiddly part (the JWT-signed API keys). It's in the utils/ folder you already copied:

sh utils/generate-keys.sh --update-env

That generates fresh values for JWT_SECRET, ANON_KEY, SERVICE_ROLE_KEY, SECRET_KEY_BASE, VAULT_ENC_KEY, POSTGRES_PASSWORD, DASHBOARD_PASSWORD, and the rest of the secret family, and writes them into .env (keeping a .env.old backup). Set DASHBOARD_USERNAME yourself — the generator leaves it at supabase. Copy the printed ANON_KEY and SERVICE_ROLE_KEY somewhere safe; your client apps need the anon key, and the service-role key belongs only on trusted servers.

Also set the URL variables to match how the stack will actually be reached — SITE_URL, API_EXTERNAL_URL, and SUPABASE_PUBLIC_URL all default to localhost.

Now boot it:

docker compose pull
docker compose up -d  #  →  http://SERVER_IP:8000 (Studio via Kong)

The pull fetches a dozen images, so the first run takes a while. When it settles, docker compose ps should show every service running or healthy, and the whole stack answers on port 8000 — the API gateway fronts both the APIs and Studio there (8443 is the gateway's HTTPS port; the pooler exposes Postgres on 5432 for session mode and 6543 for transaction mode). Log into Studio at http://SERVER_IP:8000 with your DASHBOARD_USERNAME / DASHBOARD_PASSWORD.

Exposing Studio and the API safely

Port 8000 speaks plain HTTP. Basic auth on Studio is the only thing between the internet and your dashboard, and credentials over HTTP are readable in transit — Supabase's own docs tell you to put a reverse proxy with a real TLS certificate in front of the gateway for production, and OAuth logins outright require HTTPS.

Put Caddy in front: proxy your domain to localhost:8000, let Caddy handle certificates, and firewall port 8000 so only localhost reaches the gateway directly. Then update SITE_URL, API_EXTERNAL_URL, and SUPABASE_PUBLIC_URL in .env to the HTTPS domain and restart the stack — env changes only apply on restart. If only you need Studio, consider not publishing it at all and reaching port 8000 over an SSH tunnel or VPN instead; the API is what your apps need public, not the dashboard.

Backups

Everything that matters lives in two places under your project folder:

  • volumes/db/data — the Postgres data directory. Back it up logically with pg_dump against the running database (via the pooler on port 5432, or docker compose exec db pg_dump) on a schedule, and ship the dumps off the box.
  • volumes/storage — the actual uploaded files from the Storage service. A database dump does not include these; the Storage API keeps metadata in Postgres but the bytes are on disk here. Rsync or archive this directory alongside the dumps.

A backup that lives only on the same VPS dies with the VPS. Off-box, scheduled, and restore-tested — otherwise it's a guess, and unlike the hosted platform there is no point-in-time recovery to fall back on.

What breaks compared to the cloud platform

  • No automatic upgrades. Nothing updates itself. Moving to newer service images and applying migrations is a manual, read-the-release-notes affair — take a backup first, every time.
  • Missing platform features. Branching, managed backups and PITR, advanced metrics beyond logs, ETL, analytics and vector buckets, and the platform management API are hosted-platform features that don't exist here.
  • One project per stack. The self-hosted dashboard manages a single project — no organizations, no spinning up a second project next to the first. Want another? That's another compose stack.
  • All operations are yours. Monitoring, disaster recovery, scaling, OS patching — the parts of the hosted bill you stopped paying are now paid in your time.

You're done when docker compose ps shows the stack healthy, Studio loads over HTTPS behind your proxy with the credentials you set, a test table created in Studio is readable through the REST API with your regenerated anon key, and a backup of both the database and volumes/storage exists somewhere that isn't this server.

Next steps

Search SelfHost Atlas

Search apps, comparisons, guides, and categories.

We use analytics cookies (Google Analytics, PostHog) to see which guides are useful. No ad networks, no cross-site tracking. See our privacy policy.