Dev.to WebDev 🛠 Dev 👁 0 📖 4 min read

Postgres Local Dev in 2026: The Numbers, and Lighter Alternatives to Docker Supabase

Running Postgres locally has never been easier. Running a backend locally has quietly become one of the heaviest things on your laptop. That distinction matters. If your app only talks to a database, brew install postgr

Running Postgres locally has never been easier. Running a backend locally has quietly become one of the heaviest things on your laptop.

That distinction matters. If your app only talks to a database, brew install postgresql is as light as it was ten years ago. But if your app uses Supabase (or any backend-as-a-service), it expects auth, a REST API, realtime, storage, and edge functions too. Locally, that's a stack of containers, and it's where the RAM goes.

Here are the numbers, the reason they got this way, and the alternatives, with commands to measure everything on your own machine.

The baseline: Docker Supabase

supabase start
docker stats --no-stream

The official local stack runs about a dozen containers: Postgres, PostgREST, GoTrue (auth), Realtime, Storage, imgproxy, the edge runtime, Studio, the API gateway, the meta service, and supporting services. The tinbase project's README benchmarks the stack at about 1.4 GB of RAM at boot and about a minute to boot cold, before Docker Desktop's own VM overhead.

Your numbers will differ with your Docker Desktop version, CPU architecture, and how many projects you run. That's why the measurement commands are at the end of this post: run them and see what your machine actually pays.

The old baseline: just Postgres

brew install postgresql@17
brew services start postgresql@17
createdb myproject

A native Postgres server idles in tens of megabytes and starts in about a second. Nothing about Postgres got heavier. What changed is what we expect "the local database" to include.

Why the stack got heavy

Backend-as-a-service platforms made a reasonable packaging choice: ship the local environment as the same set of services that runs in production, one container per service. Supabase, Appwrite, and Nhost all lean on Docker Compose for local development.

For parity with production, that's correct. In the cloud those services run as separate deployments, and mirroring that locally catches configuration differences early.

For a laptop, it's expensive. Every auxiliary service carries its own runtime, its own memory floor, and its own startup time, even when it sits idle for the whole session. Locally, most of those services could share one process, but the packaging doesn't make that distinction.

(The notable exception is PocketBase, which has always been a single Go binary with SQLite. It proved developers love the single-binary model; it just isn't Postgres.)

Three lighter alternatives

1. Native Postgres, and stub the rest

brew install postgresql@17
brew services start postgresql@17

Footprint: tens of megabytes, about a second to start.

Trade-off: you lose auth, realtime, storage, and edge functions. Either stub them in your code or point those calls at a hosted project. That works if your app barely uses them; it breaks the first time your code calls supabase.auth.

2. PGlite (Postgres compiled to WASM)

npm install @electric-sql/pglite
import { PGlite } from '@electric-sql/pglite';
const db = new PGlite();
await db.exec('create table todos (id serial primary key, text text)');

Footprint: the package is small and an empty database starts fast, but memory is the catch. Under real workloads the WASM heap grows to hundreds of megabytes (the tinbase README measures roughly 575–650 MB) and doesn't shrink.

Trade-off: it's a library inside your process, not a server, and it runs Postgres in single-user mode. That makes it excellent for tests (a fresh database per test file), and awkward for day-to-day work: psql, GUI clients, and connection pools need extra plumbing or won't work.

3. A single-process, Supabase-compatible backend: tinbase

npx tinbase start

Footprint (from the README): 2 processes, about 59 MB of RAM at boot, about 2 seconds to boot, with embedded native Postgres 17 on macOS and Linux.

It runs REST, auth, storage, realtime, and edge functions in one process, on port 54321 like the Supabase CLI, and the official supabase-js works unchanged. It reads your existing supabase/migrations/ folder.

Trade-off: it's alpha. It covers roughly 80% of the supabase-js surface; MFA, SSO, phone auth, and pgvector aren't supported yet; realtime DELETE events aren't filtered per row; and writes go through a single connection. It's for laptops, CI, and prototypes, not production. Full disclosure: I'm on the team.

Side by side

Approach RAM at boot Cold boot Auth / realtime / storage Client
Docker Supabase ~1.4 GB + Docker VM ~1 min Yes, full fidelity supabase-js
Native Postgres tens of MB ~1 s No pg, Drizzle, Prisma
PGlite small at start; hundreds of MB under load under 1 s No In-process
tinbase ~59 MB ~2 s Yes, ~80% coverage supabase-js

Docker Supabase and tinbase figures are from the tinbase README benchmarks. Treat all of these as ballparks and measure your own.

The decision tree

  • Need full Supabase fidelity, and Docker runs fine on your machine → Docker Supabase.
  • Only need Postgres; no auth, realtime, or storage → native Postgres.
  • Writing tests that need a fresh database each time → PGlite.
  • Want the Supabase stack without Docker's footprint → tinbase, with a Docker Supabase or hosted check before deploy.

Most teams end up with two: PGlite for tests, and one of the others for development.

Where this is heading

Hosted platforms have little reason to optimize for laptop RAM. Local development has every reason to. I expect more backend tools to ship two packagings: the production-shaped deployment, and a lightweight dev-time runtime. PGlite becoming the local engine for Prisma Postgres is an early sign. A lighter official local story from Supabase would be a big win for its community, and I'd welcome it.

Measure it yourself

# Docker Supabase: per-container memory
supabase start
docker stats --no-stream

# Native Postgres: resident memory (KB)
brew services start postgresql@17
ps -axo rss,command | grep '[p]ostgres'

# tinbase: resident memory (KB) of its processes
npx tinbase start
ps -axo rss,command | grep '[t]inbase'

Run each from a cold start and time it with time. If your numbers differ a lot from the table, leave a comment with your OS, chip, and Docker version. I'm collecting them.

📰 Read the original article on Dev.to WebDev

Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.