DEV Community

Jules Sarah
Jules Sarah

Posted on

Why We Built a Single-Process Supabase Instead of a docker-compose Stack (and What We Learned)

The moment docker-compose became the problem

Local Supabase is about a dozen containers. On a laptop that's roughly 1.4 GB of RAM at idle and about a minute to boot cold. For a backend engineer with a 32 GB machine, that's fine. It stops being fine the moment anyone else on the team needs the app running locally: a designer iterating on screens, a PM checking a flow, a CI runner with a 7 GB memory cap.

We hit that wall building RapidNative, where thousands of generated apps each need a working backend for preview. A container stack per preview was never going to work. That constraint is where tinbase came from.

What "single process" actually means

We didn't want a slimmer container stack. We wanted the backend to be a library.

In tinbase, every Supabase service is a plain (Request) => Response fetch handler: REST (PostgREST grammar), Auth (GoTrue-shaped), Storage, Realtime (Phoenix protocol over WebSocket), Edge Functions, and a Studio-style dashboard at /_/. Underneath sits a swappable database engine. On a server, one HTTP + WebSocket server wraps those handlers. In a browser, supabase-js calls them directly through a custom fetch, and no server exists at all.

The official @supabase/supabase-js works unchanged. That was a hard requirement: the moment you ship your own client, every app has to be written twice.

The numbers, from the README

Supabase local (Docker) tinbase (native engine)
Processes 12 containers 2
RAM at boot ~1.4 GB ~59 MB
Cold boot ~1 min ~2 s
Install GB of images tens of MB

Same SDK, same APIs, same migration files.

What took longer than expected

Three database engines instead of one. We started with PGlite (Postgres compiled to WASM) because it runs in a browser. Then we learned its WASM heap sits at roughly 575–650 MB and doesn't shrink. So we added embedded native Postgres 17 as the default on macOS and Linux, and a pure-JS pgmem engine (about 3.6 MB, no WASM) for the lightest browser previews. Each engine has a different trade-off, and pretending one fits every case would have been dishonest.

Wire-protocol fidelity. Keeping the real SDK working meant implementing the actual PostgREST query grammar, GoTrue-shaped responses, and the Phoenix realtime protocol. More work than a "compatible" API, but it's why apps move to hosted Supabase without edits.

Single-connection realities. PGlite is single-user mode, so writes serialize through one connection. Fine for one developer or one preview. Not a production database, and we say so.

Gotchas nobody warns you about

  • pgmem doesn't enforce RLS. It runs with superuser rights, so policies are created but not applied per request. Use it for previews, never for testing security.
  • Realtime DELETE events aren't filtered per row. INSERT and UPDATE are; DELETE isn't yet. It's in the coverage table.
  • Edge Functions with npm:, jsr:, or URL imports still need bundling. Plain functions run in-process.
  • Safari and OPFS. PGlite's OPFS filesystem needs a Web Worker, and Safari's open-file-handle limit blocks it. IndexedDB is the safe browser default.

Would we do it again? Yes, but…

Yes: npx tinbase start boots real Postgres with RLS in about two seconds, a designer can run the whole stack without knowing what Docker is, and CI jobs run RLS tests with no service containers.

But: tinbase is alpha, built for local dev, CI, prototypes, and embedded use. It covers roughly 80% of the supabase-js surface, and the gaps (MFA, SSO, phone auth, pgvector, among others) are listed openly. The right setup for most teams is tinbase on every laptop and every PR, and hosted Supabase for staging and production, with the same migration files moving between them.

The source is MIT on GitHub. If you hit an API edge we haven't covered, open an issue. Those reports are what move the coverage table.

What's your experience with the docker-compose vs single-process trade-off? Drop your setup in the comments. I'm curious how teams at different sizes land on this.

Top comments (0)