What is a BaaS and why self-host it?
A Backend-as-a-Service (BaaS) bundles everything a modern application needs on the server side into a single product: database, authentication, file storage, serverless functions, and real-time communication. Firebase popularized this model, but its proprietary nature raises concerns about cost, GDPR compliance, and vendor lock-in.
Self-hosting an open-source BaaS on your own VPS gives you full control over your data, eliminates per-request fees, and lets you meet data residency requirements. Supabase and Appwrite are the two most mature solutions in this space.
Supabase vs Appwrite — 12-criteria comparison
Scroll the table
| Criterion | Supabase | Appwrite |
|---|---|---|
| Database | PostgreSQL | PostgreSQL by default since 2.0 (MariaDB and MongoDB available) |
| Minimum RAM | 4 GB | 4 GB (+ 2 GB swap) |
| Recommended production RAM | 8 GB or more | Not published (4 GB minimum) |
| Number of containers | ~11 (13 with Logflare and Vector) | ~18 |
| Main port | 8000 (Envoy since 0.8.0, Kong before) | 80 / 443 (Traefik) |
| Admin port | 8000 (Studio via the gateway, basic auth) | 80 / console (Traefik) |
| Included services | Auth, PostgREST, Realtime, Storage, Edge Functions, Studio | Auth, Databases, Storage, Functions, Messaging, Realtime |
| Auth providers | 15+ | 30+ |
| Functions runtime | Deno only | 30+ runtimes (Node, Python, PHP, Go, Dart, Ruby, Bun…) |
| Client SDKs | JS, Python, Swift, Kotlin, Flutter, C# | JS, iOS, Android, Flutter, React Native, Web |
| Realtime | WebSocket via Realtime service | WebSocket via Appwrite Realtime |
| License | Apache 2.0 | BSD 3-Clause |
Deploying Supabase on a VPS
Step 1 — Server prerequisites
Provision a VPS with at least 4 GB of RAM (8 GB recommended for production), Docker and Docker Compose installed. Do not expose the gateway's port 8000, which serves both the API and Studio, over plain HTTP on the Internet: put an HTTPS reverse proxy (Caddy or Nginx) in front on port 443 and publish the gateway on loopback (
API_GW_HTTP_PORT=127.0.0.1:8000in.env), since Docker bypasses ufw rules. The Supavisor pooler also publishes ports 5432 and 6543: if you need no external connections, prefix them with127.0.0.1:indocker-compose.yml. A minimum 40 GB SSD disk is recommended for PostgreSQL.Step 2 — Clone the repository and configure the environment
Clone with
git clone --depth 1 https://github.com/supabase/supabase, copy the contents ofsupabase/dockerinto a project folder and copy.env.exampleto.env. Never start with the example values: generate secrets and keys withsh utils/generate-keys.shthensh utils/add-new-auth-keys.sh, and check at minimumPOSTGRES_PASSWORD,JWT_SECRET(at least 32 characters),ANON_KEY,SERVICE_ROLE_KEY,DASHBOARD_PASSWORD(Studio access) and the public URLs (SUPABASE_PUBLIC_URL,API_EXTERNAL_URL,SITE_URL).Step 3 — Start the stack
Run
docker compose up -d(orsh run.sh start, equivalent todocker compose up -d --wait). With Supabase 0.8.0+, Envoy (not Kong) serves port 8000. Check status withdocker compose psand wait until all containers arehealthy.Step 4 — Verify the installation
Access Studio through the gateway at
http://your-ip:8000(or your HTTPS domain behind the reverse proxy), with theDASHBOARD_USERNAMElogin andDASHBOARD_PASSWORDpassword. Test a simple API call withcurl http://your-ip:8000/functions/v1/hello -H "apikey: YOUR_PUBLISHABLE_KEY": the sample function's response confirms the gateway routes requests correctly.
Deploying Appwrite on a VPS
Step 1 — Server prerequisites
Appwrite requires at least 2 CPU cores, 4 GB of RAM and 2 GB of swap. Its ~18 containers (default setup) include Traefik as a reverse proxy. Ports 80 and 443 must be open.
Step 2 — Run the interactive installer
Run
docker run -it --rm --publish 20080:20080 --volume /var/run/docker.sock:/var/run/docker.sock --volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw --entrypoint="install" appwrite/appwrite:2.7.1, pinning the released version rather thanlatest. The wizard opens in your browser on port 20080: it asks for your domain, generates the keys and a tailoreddocker-compose.yml, and creates the admin account. Close port 20080 once installation is done.Step 3 — Wait for startup to finish
On first startup, the containers may take a few minutes to become operational, and the console may answer with an error in the meantime. Follow progress with
docker compose psanddocker compose logs -f appwrite. Migrations only apply when upgrading an existing instance, and are run manually withdocker compose exec appwrite migrate.Step 4 — Sign in to the console
Once installation is complete, open your domain (or the server IP) in the browser, sign in to the console with the admin account created by the wizard, then create your first project.
Authentication — Provider Comparison and OAuth
Both platforms handle email/password and phone number (SMS OTP) authentication. Appwrite supports 30+ OAuth2 providers versus 15+ for Supabase.
Supabase uses Supabase Auth (formerly GoTrue) as its authentication service — a lightweight daemon written in Go. Appwrite integrates its own Auth service configurable per project from the web console.
Important post-2.0 note: issue #13547 (https://github.com/appwrite/appwrite/issues/13547), opened in September 2026, reported that password reset and email invitations did not work on Appwrite 2.0 self-hosted deployments. It was closed without a named fix release, as the team could not reproduce it. Test these flows in a staging environment before migrating your production instance.
Serverless Functions — Deno vs 30+ Runtimes
Supabase Edge Functions run exclusively on Deno — the modern JavaScript/TypeScript runtime. Deno offers security by default (explicit permissions), native TypeScript support, and a growing module ecosystem.
Appwrite Functions support 30+ runtimes: Node.js, Python, PHP, Ruby, Go, Dart, .NET, Java, Kotlin, Swift, Bun, and more. Each runtime is an isolated Docker container. This flexibility is a major advantage for polyglot teams.
Appwrite 2.1.0 note: the _APP_LOGGING_PROVIDER and _APP_LOGGING_CONFIG_REALTIME environment variables are no longer read, and error logging goes through _APP_LOGGING_CONFIG alone, which now only accepts a Sentry DSN: a LogOwl, Raygun or AppSignal DSN is rejected at startup. Remove the old variables from your .env and check that DSN before upgrading.
Real-Time and File Storage
Real-time. Both platforms use WebSockets. Supabase exposes PostgreSQL changes in real time via its Realtime service (logical replication). Appwrite Realtime covers events on documents, files, and teams — less tied to the underlying database and therefore more portable.
A note with Supabase: the Realtime service sometimes starts before PostgreSQL is fully ready, generating the error could not connect to the database. Restarting the realtime container usually resolves this.
Storage. Supabase Storage uses an S3-compatible API with RLS access policies directly from PostgreSQL. Appwrite Storage offers team- and user-based permissions, simpler to configure.
Appwrite: console unreachable on first launch — give it time
If the Appwrite console shows an error or remains inaccessible in the first few minutes, do not blindly restart the containers: first startup can take a few minutes. Follow progress with docker compose ps and docker compose logs -f appwrite, and wait until all containers run without errors. During an upgrade, however, nothing migrates on its own: after backing up your data, run docker compose exec appwrite migrate and let it finish without stopping the containers.
Supabase: 502 Bad Gateway from gateway on startup — normal behavior
The Supabase API gateway (Kong up to 0.7.x, Envoy from 0.8.0) may return 502 errors during the first few tens of seconds after docker compose up -d. This is normal and transient — the gateway waits for PostgREST, GoTrue and other services to be ready. Wait until docker compose ps shows all containers as healthy. If 502s persist beyond a few minutes, inspect logs with docker compose logs rest and docker compose logs api-gw (the service was named kong before 0.8.0).
Appwrite 2.0: migrating from 1.x — procedure and breaking changes
Appwrite 2.0 was announced for self-hosting on September 7, 2026 (source: https://appwrite.io/changelog/entry/2026-09-07). It is a major update: PostgreSQL becomes the default database for new installs, with MariaDB and MongoDB still offered. An existing MariaDB instance stays on MariaDB after the upgrade: 2.0 does not convert your data to PostgreSQL.
Mandatory migration path. An instance older than 1.9.6 must first be upgraded to 1.9.6 (from 1.8.x, via 1.9.5 and its migration); from 1.9.6 onward, upgrading to the latest 2.x is direct. Official procedure:
1. Back up all your data: docker compose down, then copy the appwrite directory (docker-compose.yml, .env) and the named Docker volumes holding the database and files.
2. If needed, first upgrade to 1.9.6: from the parent of the appwrite directory, rerun the install command with --entrypoint="upgrade" and the appwrite/appwrite:1.9.6 image.
3. From the appwrite directory, run the migration: docker compose exec appwrite migrate.
4. Upgrade to 2.x: rerun the upgrade command with the latest released image (appwrite/appwrite:2.7.1 in November 2026).
5. Run docker compose exec appwrite migrate again — do not stop containers during this process — then check with docker ps | grep appwrite/appwrite that all containers run the same version.
Breaking changes. Issue #13547, closed in September 2026 without a named fix, reported broken password reset and email invitations in Appwrite 2.0 self-hosted. Test these flows in pre-production before migrating your main instance. In 2.1.0, _APP_LOGGING_PROVIDER and _APP_LOGGING_CONFIG_REALTIME are no longer read and _APP_LOGGING_CONFIG only accepts a Sentry DSN — remove the former and check the latter before upgrading. No rollback procedure is documented: only a full restore from backup lets you return to 1.x.
Supabase 0.8.0: Kong to Envoy — impact and TLS migration
Supabase docker-compose 0.8.0, released August 11, 2026 (source: https://github.com/supabase/supabase/blob/master/docker/CHANGELOG.md), replaces Kong with Envoy as the default API gateway (Kong remains available as an opt-in, with sh run.sh config add kong). This architectural change is transparent for most deployments, but carries a TLS breaking change: the TLS listener on port 8443 disappears.
What changes concretely. If your reverse proxy or health check scripts made direct HTTPS requests to https://your-ip:8443, those calls will fail after the update. Envoy does not re-expose this port. TLS termination must now be handled exclusively by your front-end reverse proxy.
Migration steps. Before upgrading to 0.8.0:
- Audit all references to port 8443 in your deployment scripts, health checks, and firewall rules.
- Replace them with calls to port 8000 over HTTP from the internal network, with TLS terminated by the reverse proxy.
- Upgrade with the official procedure (sh update.sh, then sh run.sh pull && sh run.sh recreate): a plain docker compose pull is not enough, since docker-compose.yml itself changes. Then check that docker compose ps shows the api-gw service (container supabase-envoy), which replaces the kong service, as healthy.
In .env, KONG_HTTP_PORT is still read as a fallback for the new API_GW_HTTP_PORT variable, and KONG_HTTPS_PORT only serves the Kong option: you can switch to API_GW_HTTP_PORT to clarify your configuration.
Decision guide — Supabase or Appwrite for your project?
Choose Supabase if: your application is web-first, your team knows PostgreSQL, you're migrating from Firebase, or you need an auto-generated REST API from your database schema (PostgREST).
Choose Appwrite if: you're building a mobile (iOS/Android) cross-platform application, your team works in multiple languages and wants to choose its function runtimes, or you prefer a more accessible admin interface without deep SQL knowledge.
On a VPS with 4 GB RAM, you are at the documented minimum of both platforms (4 GB for Supabase, 4 GB plus 2 GB of swap for Appwrite); Supabase recommends 8 GB or more in production.
In 2026, both platforms are evolving rapidly. Appwrite 2.0 makes PostgreSQL its default database for new installs — reducing Supabase's historical advantage on that point — while Supabase modernizes its gateway with Envoy. For a new installation, both are equivalent on the database question. For an existing installation (which stays on MariaDB on the Appwrite side), plan a maintenance window and test critical flows before any major update.
Common errors and fixes — both platforms
- Supabase — 502 Bad Gateway (gateway): wait for all services to be
healthy. If persistent:docker compose restart api-gw(0.8.0+) ordocker compose restart kong(< 0.8.0). - Supabase — Realtime
could not connect to the database: restart withdocker compose restart realtime. - Supabase — Invalid JWT: check that
ANON_KEYandSERVICE_ROLE_KEYare signed with the sameJWT_SECRETas in.env. - Supabase 0.8.0 — port 8443 unreachable: the Kong TLS listener was removed with Envoy. Remove direct calls to port 8443 and delegate TLS to the front-end reverse proxy.
- Appwrite — console unreachable on startup: first startup can take a few minutes. Check
docker compose psand the logs before restarting anything. - Appwrite —
docker compose down -v: DANGER — deletes all data volumes. Never run in production. - Appwrite — function timeout: cold start of an unused runtime may exceed the default timeout. Increase the limit in the function settings (Settings > Timeout, 900 s maximum).
- Appwrite 2.x — migration blocked: an instance older than 1.9.6 must first go through 1.9.6, running
docker compose exec appwrite migrateat each step. Skipping this step risks a failed migration: restore your backup and start over. - Appwrite 2.0 — password reset / invitations: issue #13547 reported in self-hosted, closed without a named fix. Test these flows before going to production.
- Appwrite 2.1.0 — dropped variables:
_APP_LOGGING_PROVIDERand_APP_LOGGING_CONFIG_REALTIMEare no longer read; remove them from.env, and keep only a Sentry DSN in_APP_LOGGING_CONFIG, the only one accepted at startup.
Conclusion: two excellent BaaS platforms, two different philosophies
Supabase and Appwrite are both solid choices for self-hosting an open-source BaaS on your VPS. Supabase shines through its PostgreSQL depth, auto-generated API, and polished Studio. Appwrite stands out for the richness of its function runtimes, and the quality of its mobile SDKs.
Both platforms went through structural updates in 2026: Appwrite 2.0 (September 2026) makes PostgreSQL the default database for new installs, Supabase 0.8.0 (August 2026) switches from Kong to Envoy. Plan your migrations with a full backup, a test environment, and validation of critical flows — especially email authentication for Appwrite 2.0. In both cases, a dedicated VPS with at least 4 GB of RAM and automated volume backups is the foundation of a reliable deployment. Starting from 99 DH/month on ServOrbit.