How I Ran the Full Supabase Stack Locally in 100 MB of RAM (No Docker)
Twelve containers. 1.6 GB of RAM. 45 seconds to supabase start. Every time I opened my project.
This week, the same stack runs in ~100 MB, boots in 2.5 seconds, and my app code didn't change. Here's how.
Why this matters
Docker overhead for local dev is a background tax that's easy to ignore. My M1 Air fans spin up whenever Supabase is running. My battery drains ~30% faster. My other tools (VS Code, browsers, IDE plugins) share the remaining RAM with 12 containers I don't think about.
The real cost isn't the RAM itself, it's the workflows that get shaped around it:
- I don't run local backup unless I know I'll code for >30 min (not worth the boot).
- When I'm designing a schema change, I test it against hosted Supabase because local is slow.
- Anyone on my team who isn't a full-time backend engineer (designers, PMs) uses staging instead of local, which creates its own dysfunction.
The alternative: a single-binary Supabase-compatible backend
The tool I ended up using is Tinbase — a single-file executable (~58 MB) that runs real Postgres 17 plus auth, realtime, edge functions, webhooks, and cron. It exposes the same HTTP + Postgres endpoints as hosted Supabase, so supabase-js connects to it unchanged.
From my app's perspective, the only change was the connection string:
ts
// Before (Docker Supabase local)
const supabase = createClient(
'http://localhost:54321',
'eyJhbGci...anon-key'
);
// After (Tinbase)
const supabase = createClient(
'http://localhost:4000',
'eyJhbGci...anon-key' // same SDK, same anon key format
);
That's it for app code. Zero refactor.
The migration steps
1. Install
bash
curl -fsSL https://tinbase.dev/install.sh | sh
One executable at ~/.tinbase/bin/tinbase. ~58 MB on disk.
2. Initialize a project
bash
tinbase init my-project
cd my-project
tinbase start
Boots in ~2.5s. Exposes:
- Postgres on localhost:5432
- REST API on localhost:4000/rest/v1
- Auth on localhost:4000/auth/v1
- Realtime WebSocket on localhost:4000/realtime/v1
- Storage on localhost:4000/storage/v1
- Admin UI on localhost:4000
Same endpoint structure as hosted Supabase.
3. Port migrations
bash
cp -r /path/to/my-app/supabase/migrations ./migrations
tinbase db reset
The migrations run against the embedded Postgres. RLS policies work identically because it's real Postgres 17.
4. Update the connection string in my app
[7:04 AM]Change the SUPABASE_URL env var from http://localhost:54321 to http://localhost:4000. That's the only app-side change.
5. Stop Docker
bash
supabase stop
docker system prune -a --volumes # reclaim ~2.3 GB of images + volumes
Benchmarks after the switch
Measured on the same M1 Air, same app, same test load (10 min of active development: a few queries, a realtime subscription, one edge function invocation).
| Metric | Docker Supabase | Tinbase | Delta |
|---|---|---|---|
| Boot time (cold) | 45s | 2.5s | −94% |
| Boot time (warm) | 15s | 1.2s | −92% |
| Steady-state RAM | 1626 MB | 98 MB | −94% |
| Disk footprint | 2.3 GB | 58 MB | −97% |
| Idle CPU % | 4-8% | 0.1-0.4% | −95% |
| Fan noise | audible | silent | qualitative |
The RAM number is the headline. Under active load with realtime + edge functions firing, RAM peaked at ~250 MB. Docker Supabase under the same load peaked north of 2 GB.
What worked out of the box
- supabase-js SDK: unchanged. All CRUD, auth, realtime, storage calls work identically.
- RLS policies: identical behavior (it IS real Postgres 17, same policy engine).
- Edge functions: deployed the same way (tinbase functions deploy hello), runtime is Deno like Supabase.
- Realtime subscriptions: WebSocket connection, same protocol.
- Storage: same bucket/RLS semantics.
- Migrations: standard Postgres SQL, ran without modification.
What I had to change
Being honest — a few things weren't 100% drop-in:
1. Supabase Studio → Tinbase Admin UI. The admin UI is different (obviously — it's a different product). If my team relied on Studio-specific features, I'd have to check compat. In my case, I mostly use it for eyeballing schema, which the Tinbase UI does fine.
2. Some newer Supabase-hosted features aren't there yet. Specifically, some of the newer analytics endpoints and the vector search integration are hosted-only for now. If my app depended on these, I'd need to keep them talking to hosted Supabase (Tinbase for the general dev workflow, hosted Supabase for those specific features).
3. Edge function environment isn't 100% identical. Deno version, some environment variables, some Supabase-specific runtime globals. I hit one edge case where I was using Deno.env.get('SUPABASE_URL') — worked in the local runtime, but the value was different. Easy fix, but worth mentioning.
Where the single-binary approach hits limits
This isn't a Supabase replacement. It's a local-dev replacement.
For production, hosted Supabase is still the right choice — you want their SLA, their scaling infrastructure, their team maintaining it. What Tinbase replaces is the local-dev docker-compose stack.
The approach starts to strain if:
- You have multi-terabyte data volumes locally: fine for dev-sized datasets, less optimized for large ones (though it's real Postgres, so it scales as Postgres does).
- You need every hosted-Supabase feature identically: check the compat matrix.
- You're running this in prod at scale: it can, but that's not the primary use case.
For a workflow where I want the Supabase ergonomics without the Docker overhead — which is 95% of my local dev — it's genuinely better.
Takeaway
If you're on Docker Supabase for local dev and the RAM/boot-time overhead is a background tax, there's a lighter alternative now. Same SDK, same auth/realtime/storage semantics, one file. Migration is a connection string swap.
The Docker overhead was invisible to me until I removed it. Now the difference is stark: fans off, faster iteration, and (unexpectedly) designers on my team started running the app locally because the setup was one command.
Sometimes the right optimization isn't a smaller container image. It's asking whether you need the container abstraction at all.
Top comments (0)