# Benchmarking Local Postgres for Supabase Devs: Docker vs. PGlite vs. tinbase

## Table of contents

1.  Why local Postgres matters for Supabase devs
    
2.  The three contenders
    
3.  Test methodology
    
4.  Results
    
5.  Compatibility matrix
    
6.  When to pick which
    
7.  What's next
    

## Why local Postgres matters

Every Supabase-compatible dev workflow depends on a local Postgres. What differs is how heavy that Postgres runtime is on your laptop.

The `supabase start` docker-compose stack is the reference implementation: 12 containers, roughly 2.3 GB of install footprint, and around 1.6 GB of memory under load. It is production-parity by design, and you pay for that parity in resident memory every hour your editor is open.

Two lighter alternatives have appeared. PGlite compiles Postgres to WASM and runs it in-process. tinbase packages real Postgres plus auth, storage, and realtime into a single executable.

Each makes different tradeoffs. This post benchmarks all three on a common workload so you can pick deliberately rather than by default.

> The interesting question is not which one is fastest. It is which one is fast enough while leaving you enough RAM to run the rest of your machine.

## The three contenders

**Docker Supabase.** The official local stack: Postgres, Studio dashboard, GoTrue auth, Realtime, Storage, the Edge Functions runtime, and a Kong gateway, across 12 containers on docker-compose. Exact parity with hosted Supabase is the whole point.

**PGlite.** Postgres compiled to WASM, roughly a 3 MB bundle, running in-process in Node, Deno, or the browser. No auth, no realtime, no storage. Just SQL. Ideal for tests, notebooks, and offline-first apps that need a genuine Postgres in the browser rather than a SQL-shaped imitation.

**tinbase.** A single executable that runs embedded Postgres 17 alongside GoTrue-compatible auth, the Storage API, realtime, and edge functions in one process. The official `supabase-js` SDK points at it unchanged. MIT licensed, and currently at v0.14.0 alpha, which the project describes as suited to local development, prototypes, and embedded or browser use rather than production.

One nuance worth flagging before the numbers: these three are not cleanly separate. [tinbase](https://www.tinbase.dev/?utm_source=hashnode&utm_medium=blog&utm_campaign=local-postgres-benchmark-docker-pglite-tinbase) ships multiple database engines, and one of them is PGlite. The WASM engine is how tinbase runs inside a browser tab. So when this benchmark compares PGlite against tinbase, it is really comparing bare PGlite against tinbase's native embedded Postgres engine, not two unrelated projects.

Starting tinbase is a single command, and it serves on the same port the Supabase CLI uses:

```bash
npx tinbase start
# supabase-js connects at http://127.0.0.1:54321
```

## Test methodology

**Hardware:** MacBook Air M2, 16 GB RAM, macOS 15.3.

**Schema:** a small e-commerce dataset. Four tables (users, products, orders, order\_items) totalling 12 columns. Seeded with 10k users, 1k products, 50k orders, and 200k order\_items.

**Workloads:**

1.  1,000 sequential inserts into `orders`
    
2.  1,000 sequential selects joining users to orders
    
3.  100 concurrent mixed inserts and selects
    

**Metrics:** cold boot time, idle RAM after boot, workload latency (median and p99), and disk footprint. Every measurement was run three times with the median reported.

## Results

| Metric | Docker Supabase | PGlite | tinbase |
| --- | --- | --- | --- |
| Cold boot time | 44 s | 0.3 s | 2.5 s |
| Idle RAM | 1,626 MB | 42 MB | 98 MB |
| Disk footprint | 2.3 GB | 3 MB | 58 MB |
| 1,000 inserts (median) | 480 ms | 620 ms | 510 ms |
| 1,000 selects with join (median) | 210 ms | 380 ms | 240 ms |
| 100 concurrent mixed (p99) | 180 ms | N/A (single-writer) | 210 ms |
| Handles concurrent writes | Yes | No | Yes |

Docker Supabase takes raw throughput by a slim margin on selects. Its Postgres runs on native Linux inside the Docker VM, which edges out tinbase's macOS-native build. That margin is 30 ms on a 210 ms query, against a 16x difference in resident memory.

PGlite is meaningfully slower on complex queries, which is the expected cost of WASM, and it cannot handle concurrent writers at all. Its WAL is single-threaded, so workload three simply does not apply.

> A 14 percent latency penalty in exchange for 1.5 GB of reclaimed RAM is not a close call on a 16 GB laptop.

## Compatibility matrix

| Feature | Docker Supabase | PGlite | tinbase |
| --- | --- | --- | --- |
| Standard Postgres SQL | ✅ | ✅ | ✅ |
| Row-Level Security | ✅ | ✅ | ✅ |
| `supabase-js` auth | ✅ | ❌ | ✅ |
| Realtime subscriptions | ✅ | ❌ | ✅ |
| Edge functions | ✅ | ❌ | ✅ |
| Storage buckets | ✅ | ❌ | ✅ |
| Studio web dashboard | ✅ | ❌ | ✅ |
| Concurrent writers | ✅ | ❌ | ✅ |

The Studio row deserves a note, because it is the row most people get wrong about tinbase. A Supabase-Studio-style dashboard ships in the binary at `/_/`, covering the table editor, SQL, auth, RLS policies, storage, and live logs. It is not a separate process and it is not something you install. If you assumed single-binary meant dropping to `psql`, that assumption is out of date. The [tinbase Studio tour](https://www.tinbase.dev/studio?utm_source=hashnode&utm_medium=blog&utm_campaign=local-postgres-benchmark-docker-pglite-tinbase) shows the current surface.

On RLS specifically, all three behave the same way for policy evaluation: requests run with your JWT claims applied, so `auth.uid()` policies work without modification. That is the compatibility detail that matters most in practice, because RLS is where local-versus-hosted divergence usually bites.

Extension support is where the matrix gets fuzzier than a checkmark can express, so verify against the [docs](https://www.tinbase.dev/docs?utm_source=hashnode&utm_medium=blog&utm_campaign=local-postgres-benchmark-docker-pglite-tinbase) for whichever extension your project actually depends on. Worth knowing: tinbase implements database webhooks, cron scheduling, and a queue subset natively, so several things you would reach for `pg_net`, `pg_cron`, or `pgmq` to do on hosted Supabase do not need an extension at all.

## When to pick which

**Docker Supabase.** Pick it when you need production parity for release-week testing, when your team is large enough that everyone shipping to hosted Supabase should be testing against the identical stack, or when you live in Studio and do not want to context-switch. Parity is a real feature and the memory cost buys something.

**PGlite.** Pick it for tests that need a real Postgres per test run, or for offline-first apps that ship a Postgres to the browser. It is not a general-purpose dev database, and trying to use it as one means discovering the single-writer limit at an inconvenient moment.

**tinbase.** Pick it when you want Supabase dev ergonomics without paying 1.6 GB of idle RAM. It is strongest on machines with 16 GB or less, and for demos and workshops where you cannot assume Docker is installed on anyone's laptop. Because migrations are read from `supabase/migrations/*.sql` and tracked in the same table the Supabase CLI uses, the exit path is intact: outgrow it and push the same files to hosted Supabase.

The alpha status is the honest caveat. At v0.14.0 the project is explicit that it is not production-ready, so treat it as a local dev and prototyping tool and keep your production path pointed elsewhere for now.

## What's next

All three are converging on the same idea from different directions: local development does not need to look like production infrastructure to be useful.

Docker Supabase is unlikely to shed its container footprint, because that footprint *is* the parity guarantee. PGlite is pushing further into the browser. tinbase is working toward production readiness, and the direction to watch is whether the single-process model holds up once real deployment pressure lands on it. The [published footprint benchmarks](https://www.tinbase.dev/?utm_source=hashnode&utm_medium=blog&utm_campaign=local-postgres-benchmark-docker-pglite-tinbase#benchmarks) are reproducible from the repo if you want to run them on your own hardware rather than trust mine.

My own workflow settled on tinbase for everyday development and Docker Supabase for release-week testing. Yours will vary with team size and how much time you spend in Studio.

If you run this benchmark on different hardware, particularly on Linux where the Docker VM penalty disappears, the select-throughput gap should narrow. I would be curious to see those numbers.
