Run docker stats on a typical full-stack laptop in 2026 and you'll see something like this:
NAME CPU % MEM USAGE / LIMIT MEM %
supabase-db 2.3% 680 MiB / 15.9 GiB 4.2%
supabase-auth 0.5% 130 MiB / 15.9 GiB 0.8%
supabase-storage 0.3% 95 MiB / 15.9 GiB 0.6%
... 9 more containers ...
Add it up and a full local Supabase stack idles somewhere around 1.5 GB of RAM before you write a single line of application code. On an M-series MacBook with memory to spare, fine. On a 16 GB work laptop already running an IDE, Slack, a browser, and DevTools, it's not.
Docker isn't wrong. It's just often overkill for local development, and the ecosystem has quietly grown alternatives worth reaching for.
The three real reasons Docker is still the right answer
Before dumping Docker, be honest about the cases where it's the correct choice:
- You genuinely need production parity. Your prod runs in Kubernetes; you want the same image locally to catch container-config bugs. Fair.
- You're building tools that ship as containers. Docker locally is the SDLC.
- You need process isolation with real network boundaries. Testing service-mesh routing, sidecar patterns, and similar.
Everything else (a Postgres plus Redis for your Next.js app, a local dev database for your side project, a Postgres to run migrations against) doesn't need Docker's isolation guarantees.
Alternative 1: Native installs
The most underrated 2026 answer:
brew install postgresql@17 redis
brew services start postgresql@17
- Fastest boot: no container startup overhead, Postgres is up in a couple of seconds.
- Lowest RAM: an idle native Postgres sits in the tens of megabytes, versus the full containerized backend stack measured in gigabytes.
- Native OS integration: starts on login via
brew services, no daemon in between.
The downside: you get exactly one Postgres version at a time. If you juggle multiple projects on different Postgres majors, this gets awkward. Which is what the tools in Alternative 3 exist for.
Alternative 2: Single-binary embeds
A newer category: databases and services shipped as single executables.
-
SQLite has been doing this forever.
sqlite3 my.dbis a few megabytes and zero config. - PGlite compiles Postgres to WASM: roughly 30 MB, runs in Node or the browser. Great for tests and prototypes.
- Tinbase bundles real Postgres 17 plus auth, realtime, and edge functions into a single executable (58 MB binary, about 92 MB installed). MIT-licensed, works with the supabase-js SDK unchanged, no Docker daemon required.
The value isn't the binary size. It's that a single file is trivially reproducible across a team: download one executable and you're running. No docker-compose.yml drift, no daemon, no port fights with someone else's Postgres container.
Alternative 3: Micro-runtimes (Nix, mise, devbox)
The reproducible-environment problem Docker mostly solved for backend, tools like Nix, mise, and devbox solve for local dev without containerization:
- Nix flakes: declarative, exact-version pinning of Postgres, Node, whatever. Steep learning curve, rock-solid results.
-
mise: simpler, per-project
.mise.tomlfor language versions and tools. - devbox: Nix underneath with a friendlier UX.
You still run the binaries natively (no container overhead) but with the reproducibility Docker was hired for.
Comparing on metrics that matter
For a local dev environment running Postgres plus auth plus realtime for a small app:
| Approach | RAM idle | Cold start | Team reproducibility |
|---|---|---|---|
| Docker Compose (Supabase local) | gigabyte-class | tens of seconds | High (docker-compose.yml) |
| Brew install (Postgres alone) | tens of MB | seconds | Low (manual setup) |
| Single-binary Postgres runtime | ~100 MB-class | seconds | High (one file, one checksum) |
| Nix + native Postgres | tens of MB | seconds | High (flake.nix) |
The Docker column isn't wrong, and it wins on "everyone already knows it." But it's paying for reproducibility with more than a gigabyte of RAM that the alternatives deliver for a fraction of that.
Worked example: replacing a Supabase docker-compose
The typical Supabase local setup is a dozen containers. A replacement that keeps the ergonomics (Postgres, supabase-js compatibility, auth, realtime) but ships as one file:
# Before: docker-compose up --wait (a dozen containers, gigabyte-class RAM)
# After: ./tinbase (one process, under 100 MB)
Your app code doesn't change. It's the same supabase-js SDK, pointed at the local URL Tinbase prints on startup:
import { createClient } from '@supabase/supabase-js'
const supabase = createClient(
'http://localhost:PORT', // from tinbase startup output
'ANON_KEY' // also printed on startup
)
The SDK doesn't know the difference. Your app code stays identical. Your laptop gets a gigabyte-plus of RAM back.
Closing: pick the runtime that fits the workload
"Docker-first for everything" was a 2018 answer. In 2026, the answer is "pick the runtime that fits the workload." Prod-parity work: Docker. Local backing services: native or single-binary. Bespoke tools: whatever ships fastest.
None of the alternatives obsolete Docker. They just recover the RAM and startup time you were paying for reproducibility you weren't actually cashing in.
What does your local stack look like in 2026: still docker-compose, fully native, or something in between? Drop your setup in the comments, especially if you've replaced a heavy compose file with something smaller.
Top comments (1)
The insights on native installs and single-binary runtimes are spot on, especially considering how resource-intensive Docker can be for local development. It's fascinating to see alternatives like Tinbase leveraging lightweight executables for rapid setup—this could greatly enhance developer productivity without the overhead of containers. If you're exploring ways to optimize the development experience further, I'd be interested in discussing potential contributions, particularly in integrating micro-runtimes for more streamlined environments. Have you considered any specific tools or workflows for managing multiple versions of Postgres seamlessly?