Essays

Analysis

Which database should your product actually use?

A first-principles analysis of SQLite vs PostgreSQL — with benchmarks, real company data, and the arithmetic.

André Casal25 min read

"Just use Postgres" is one of the most common pieces of advice in software engineering. But I realized I'd never seen anyone actually decompose the question — break down what factors matter, how much they matter, and examine the data for each one. The advice is repeated because it sounds right, not because anyone does the analysis.

So I did the analysis. I decomposed the question "SQLite or PostgreSQL?" from first principles, ran benchmarks on real databases, read the engineering disclosures from companies at scale, and did the arithmetic.

What I found surprised me.

Throughput

"Can my database handle the load?" — let's find out.

Where the bottlenecks really are

A request passes through three layers: network → application server → database. How much traffic can each handle?

Network. Your server's bandwidth depends on your hosting provider. Hosting falls into two categories: infrastructure providers own and operate physical servers; platform providers deploy on top of them and inherit their network limits.

Some infrastructure providers own entire data centers — the building, power, cooling, and network backbone. Others practice colocation: they own the servers but house them in a third-party facility that supplies power, cooling, and physical security. Either way, what matters is the bandwidth allocated to your VM — which providers cap well below the physical NIC's capacity:

INFRASTRUCTURE PROVIDERS (own and operate hardware)

┌─ Azure ── servers + data centers ────────────────────────────────────────┐
│  100 Gbps NIC ────────── Azure rate-limits to ──────────► 12.5 Gbps/VM  │
└──────────────────────────────────────────────────────────────────────────┘
┌─ GCP ── servers + data centers ──────────────────────────────────────────┐
│  100 Gbps NIC ────────── GCP rate-limits to ────────────► 8 Gbps/VM     │
└──────────────────────────────────────────────────────────────────────────┘
┌─ Hetzner ── servers + data centers ──────────────────────────────────────┐
│  10 Gbps NIC ── shared across VMs (no per-VM cap) ──────► ~6 Gbps/VM    │
└──────────────────────────────────────────────────────────────────────────┘
┌─ AWS ── servers + data centers ──────────────────────────────────────────┐
│  25 Gbps NIC ────────── AWS rate-limits to ─────────────► 1.56 Gbps/VM  │
└──────────────────────────────────────────────────────────────────────────┘
┌─ Fly.io ── servers in colocation ────────────────────────────────────────┐
│  ~10 Gbps NIC ────────── Fly.io rate-limits to ─────────► ~1.5 Gbps/VM  │
└──────────────────────────────────────────────────────────────────────────┘

WHAT YOUR APP GETS

  Deploy directly (you get a VM — per-VM limit applies):

  Provider          Your app gets
  ────────          ─────────────
  Azure             12.5 Gbps
  GCP               8 Gbps
  Hetzner           ~6 Gbps
  AWS               1.56 Gbps
  Fly.io            ~1.5 Gbps

  Deploy on a platform (upper-bounded by infrastructure NIC):

  Platform          Runs on          Bandwidth
  ────────          ───────          ─────────
  Render (US)       GCP              undocumented (≤ 100 Gbps NIC)
  Render (EU)       AWS              undocumented (≤ 25 Gbps NIC)
  Supabase          AWS              undocumented (≤ 25 Gbps NIC)
  Neon              AWS              undocumented (≤ 25 Gbps NIC)
  Turso             AWS              undocumented (≤ 25 Gbps NIC)
  Vercel            AWS (Lambda)     ~600 Mbps/function (benchmarked)
  Netlify           AWS (Lambda)     ~600 Mbps/function (benchmarked)

All per-VM bandwidth at 4 vCPUs. Platform providers use their own
instance types and architecture — their actual bandwidth is unknown
but cannot exceed their infrastructure provider's physical NIC.

How many requests per second is that? A dynamic, database-backed request-response — not counting static assets served from CDN — carries:

  • Request: HTTP headers (~800 bytes) + optional POST body (~200–500 bytes). Total: ~1 KB.

  • Response: headers (~500 bytes) + body. A JSON API response: median ~2–5 KB. Server-rendered HTML: ~30–40 KB uncompressed, ~10–15 KB gzipped.

  • Protocol overhead (TCP/IP headers, TLS framing): ~3–5%.

Total on the wire: ~4 KB for a JSON API round-trip, ~14 KB for server-rendered HTML.

ProviderBandwidthJSON API (~4 KB)HTML (~14 KB)
Azure12.5 Gbps~391K req/s~112K req/s
GCP / Render (US)8 Gbps~250K req/s~71K req/s
Hetzner~6 Gbps~188K req/s~54K req/s
AWS / Vercel / Supabase / ...1.56 Gbps~49K req/s~14K req/s
Fly.io~1.5 Gbps~47K req/s~13K req/s

Application server. A server running real business logic — authentication, validation, serialization, template rendering — handles roughly 10K–50K req/s. Bare-metal benchmarks show 100K+ for trivial handlers (TechEmpower Round 22), but production workloads with middleware and business logic are 5–20× slower.

Database. From the benchmark (methodology and full results below):

OperationSQLitePostgreSQL (1 conn)PostgreSQL (16 conn)
Sequential writes23,403/s7,740/s35,370/s
Mixed 80/20 read/write96,051/s11,824/s

Web apps are 80–97% reads (data from GitHub, Slack, Meta — detailed below).

Here's how to handle each limit as you hit it.

App server CPU saturates. Add app servers behind a load balancer. Both databases route all writes to a single primary — PostgreSQL natively, SQLite via Turso/libSQL or LiteFS. The architecture is equivalent.

Read latency increases. Application-level caching (in-memory or Redis), read replicas, CDN for cacheable responses. Both databases support all of these. SQLite with embedded replicas has an edge — reads stay in-process on each app server, avoiding the network round-trip.

Write throughput approaches the primary's limit. Scale vertically — a faster CPU increases write throughput with zero architecture changes. No new processes, no connection pooler, no migration.

Single-core writes exhausted (SQLite ~23K writes/sec). This is the only point where the database choice matters for throughput. PostgreSQL can parallelize writes across CPU cores using concurrent connections. But note: PostgreSQL's single-connection throughput (~8K writes/sec) is lower than SQLite's ~23K — so switching to PostgreSQL only helps if you also configure connection pooling and concurrent writers. You're not just changing databases; you're adding infrastructure.

Reaching this point requires sustained write volumes that only the most write-heavy applications at 10M+ DAU would generate. At every step before this one, SQLite gives you more headroom with less complexity.

The rest of this section provides the supporting data: how much throughput products actually need, what companies at scale report, and the benchmark numbers behind these claims.

Capabilities

TODO: Content coming soon.

User-facing Latency

TODO: Content coming soon.

Development Velocity

TODO: Content coming soon.

How to Decide

TODO: Content coming soon.


Benchmark repository: sqlite-vs-postgres-benchmark

Full company throughput research with citations: database-throughput-research.md

Get notified when I publish

One deep essay per month on code architecture, product strategy, and first-principles thinking. Each one takes weeks to research. No fluff, no spam.