Why self-host Supabase on a VPS
Supabase is not a black box: it's a set of open source services (PostgreSQL, GoTrue for auth, PostgREST for the API, Realtime, Storage, Kong as the gateway) orchestrated together. The cloud version bills per project and per active user, and pauses inactive free projects. By self-hosting on a VPS, you get a complete backend-as-a-service without these limits, with direct access to your PostgreSQL for migrations, extensions (pgvector, PostGIS), and tuning. It's ideal for an agency hosting several client apps, a startup SaaS wanting to control its costs, or any project requiring data sovereignty. You handle updates and backups yourself, in exchange for total freedom.
The concrete benefits of a self-hosted Supabase
- Complete backend: Postgres database, Auth, Storage, Realtime, and REST/GraphQL API in one stack.
- Direct access to the underlying PostgreSQL: SQL migrations, extensions, and RLS policies without limit.
- No pausing and no billing per monthly active user.
- Studio included: a web administration interface to manage tables, auth, and buckets.
- AI extensions via
pgvectorfor RAG and semantic search, enabled freely. - Multiple projects on a single VPS: convenient for an agency or a development studio.
Hardware and software prerequisites
Self-hosted Supabase launches around a dozen containers (Postgres, Kong, GoTrue, PostgREST, Realtime, Storage, Studio, Meta, Imgproxy...), so it's a more demanding stack than a simple database. Count on a minimum of 2 vCPU and 4 GB of RAM for a test environment, and rather 4 vCPU with 8 GB of RAM and 80 GB of SSD for production. On the software side: Ubuntu 22.04/24.04 LTS, Docker and Docker Compose v2, a domain name pointing to the VPS (e.g., api.mysaas.com) to expose the gateway over HTTPS, and a reverse proxy like Caddy, Traefik, or Nginx to handle SSL. Also plan to generate robust secrets (JWT_SECRET, anon and service_role keys).
Deploying self-hosted Supabase with Docker
Clone the official repository and prepare the environment
Get the docker folder from the Supabase repository, copy .env.example to .env, then generate unique secrets: POSTGRES_PASSWORD, JWT_SECRET, and the ANON_KEY / SERVICE_ROLE_KEY keys derived from the JWT. Never deploy with the example values.
Configure the URLs and the Studio password
In .env, set SITE_URL, API_EXTERNAL_URL, and SUPABASE_PUBLIC_URL with your domain, and protect Supabase Studio with DASHBOARD_USERNAME and DASHBOARD_PASSWORD: the Studio gives full admin access to your project.
Launch the stack
Start with docker compose up -d, then follow the services' startup via docker compose ps. The first Postgres initialization and the application of internal migrations take one to two minutes; check for the absence of errors in docker compose logs.
Set up a reverse proxy with SSL
Put Caddy or Traefik in front of Kong (the gateway's port 8000) to expose your API over HTTPS. Caddy automatically obtains and renews Let's Encrypt certificates: a simple mysaas.com { reverse_proxy localhost:8000 } block is enough. Never expose Postgres (5432) publicly.
Enable RLS policies
Log in to the Studio, create your tables, then enable Row Level Security on each one and define your policies. Without RLS, your anon key exposes all your data: this is the number one security point of a self-hosted Supabase.
Back up Postgres and the Storage
Schedule a daily pg_dump of the database and archive the Storage volume (file buckets) to external storage. Test a full restore on a staging VPS before relying on it in production.
Supabase vs Appwrite: which self-hosted BaaS to choose?
| Criterion | Supabase | Appwrite |
|---|---|---|
| Database | Native PostgreSQL, full SQL | MariaDB internally, document/collection API |
| Data model | Relational, Postgres RLS | Documents and collections, rule-based permissions |
| Auto-generated API | REST (PostgREST) + GraphQL | REST, GraphQL, and multiple SDKs |
| Authentication | GoTrue, OAuth, magic links | Built-in Auth, OAuth, teams, JWT |
| Resource footprint | Heavier (~10 containers) | Moderate, more compact stack |
| Vector / AI search | Native via pgvector | Not native, must be externalized |
| Ideal for | Relational apps, RAG, advanced SQL | Document-oriented mobile/web apps |
| Learning curve | Comfortable if you know SQL | Fast for front-end/mobile developers |
Update Supabase with caution: since the images are versioned in the docker-compose.yml, always take a full pg_dump and a VPS snapshot before a docker compose pull && docker compose up -d. Some version upgrades touch the internal schema of the services. For production, isolate Postgres on a dedicated high-IOPS volume and enable pgvector from the start if you plan on semantic search: adding it later requires a schema migration.