TL;DR
-
supabase startruns the local stack in Docker containers. It's the most complete option, and the heaviest. - You can wire Postgres, PostgREST and auth together natively, but then you own the orchestration.
- Single-process, Supabase-compatible backends now exist. They keep
supabase-jsunchanged but cover a subset of features. - Pick based on which Supabase features your app actually uses.
Option 1: The Supabase CLI (Docker)
This is the official path. supabase start boots Postgres, auth, the REST API, realtime, storage, Studio and supporting services as containers.
It's the closest match to hosted Supabase, which is its biggest strength. The cost is resources. In a benchmark published on Apple Silicon (macOS 15), the Docker stack is described as 12 containers, about 2.3 GB installed, and around 1.6 GB of memory. On a 32 GB workstation you won't notice. On a 16 GB laptop running an editor, a browser and a simulator, you will.
Choose it when: you use storage image transforms, MFA, SSO, phone auth, or anything recently shipped on hosted Supabase.
Option 2: Wire it up natively
Every core piece is open source and runs outside Docker: Postgres from Homebrew or your package manager, PostgREST for the REST API, and the auth server.
brew install postgresql@17
brew services start postgresql@17
# then run PostgREST and the auth server as separate processes,
# each with its own config pointing at your database
It's light on resources, but you now own the setup: configs, versions, process management and keeping them in sync with what supabase-js expects.
Choose it when: you only need one or two pieces (often just Postgres + PostgREST) and like owning the stack.
Option 3: A single-process, Supabase-compatible backend
The newer option bundles the API surface into one process. I work on Tinbase, which is one of these, so take this section with that in mind.
npx tinbase start
It prints the API URL (http://127.0.0.1:54321) and the anon and service_role keys, then you point the normal client at it:
import { createClient } from '@supabase/supabase-js';
const supabase = createClient('http://127.0.0.1:54321', ANON_KEY);
What carries over:
-
supabase/migrations/*.sqlandsupabase/seed.sqlare applied on start - RLS policies run with JWT claims, so
auth.uid()behaves as on hosted Supabase - REST, auth (email/password, anonymous, magic links, OAuth), storage, realtime, edge functions, cron
- A Studio dashboard at
/_/andtinbase gen types typescriptfor typed clients
What it doesn't do (from its README): MFA, SSO/SAML or phone auth; image transformations or resumable uploads; aggregates in select. It handles one connection at a time and is labelled alpha, not production-ready. It's a local-dev and prototyping tool, not a hosting replacement.
Its benchmark lists about a 2-second start and a memory footprint in the tens of MB with the default embedded Postgres 17 engine.
Choose it when: you use the core Supabase APIs, want a fast cold start, and deploy to hosted Supabase anyway.
Quick comparison
| Supabase CLI (Docker) | Native, hand-wired | Single-process (e.g. Tinbase) | |
|---|---|---|---|
| Setup | One command | Several services | One command |
| Footprint | Heaviest | Light | Light |
| Feature coverage | Full | What you wire up | Core APIs, documented gaps |
| Maintenance | Low | High | Low |
| Production use | Local only | Your call | Not production-ready |
How to decide
- List the Supabase features your app calls. Check them against the gaps above.
- If everything is covered, try the lighter option on a branch and run your test suite against it.
- Keep the Docker stack around for the features that need it. Both can read the same
supabase/folder.
Which Supabase feature is the one keeping you on Docker locally?
Top comments (0)