Postgres vs SQLite for Small Apps in 2026

Sanchez Kim
Sanchez Kim
AI Engineer · · 7 min read

In 2026 the SQLite-vs-Postgres question flipped: it's no longer whether SQLite is production-ready, but whether your app actually has the concurrent-write volume that needs Postgres. A pragmatic decision guide with current versions, the single-writer trade-off, 2026 tooling (Litestream, Turso), a real cost table, and a migration trigger based on evidence rather than anticipation.

#sqlite#postgresql#databases#indie-dev#litestream#turso#saas
Postgres vs SQLite for Small Apps in 2026

**

The question used to be whether SQLite was serious enough to run in production. In 2026 that's settled, and the real question flipped: does your app actually have the concurrent-write volume that needs Postgres?

For most small, read-heavy apps the honest answer is no. But there are a few lines where Postgres still wins outright, and knowing exactly where they are saves you from both premature complexity and a painful migration later.

The one fact that decides it

Everything comes down to how each engine handles concurrent writes.

SQLite in WAL mode gives you unlimited concurrent readers plus exactly one writer at a time. Readers don't block the writer and the writer doesn't block readers — everyone reads a consistent snapshot. The catch is the ceiling: a second write that arrives while another is in flight either waits or fails with SQLITE_BUSY.

You can soften that with a sensible busy_timeout, but the model is fundamentally one-writer-at-a-time.

Postgres uses MVCC built for many simultaneous writers. Dozens of connections can commit at once without a global write lock — though writes to the same row still serialize — and it's designed for heavy relational workloads with complex JOINs across many tables.

Two-panel diagram contrasting SQLite's unlimited readers with one writer against Postgres MVCC accepting many concurrent writers

That's the whole decision in one paragraph. If your write pattern is low or bursty and reads dominate — a blog, a docs site, a dashboard, most early SaaS — SQLite's single writer is rarely the thing you hit first. If you have many users writing concurrently and continuously, Postgres earns its keep.

What 2026 fixed for SQLite

The old objections to SQLite in production were about operations, not the engine: no backups, no replication, no edge story. Those gaps are largely closed now.

Litestream

does continuous streaming replication — it ships your WAL pages to S3-compatible storage so you get point-in-time disaster recovery on a single node. The v0.5.0 rewrite replaced the shadow-WAL storage layer with the LTX format, where each LTX file is a sorted changeset of pages for a period of time — which is what makes point-in-time restore work by compaction instead of WAL replay.

And the LTX design is meant to make replicating a whole directory of databases practical — the Litestream team frames it as "it should thus be possible", so verify it against the version you install rather than assuming it ships today. For a single-server app, this is the backup answer that used to be missing.

Flow diagram of the Litestream LTX replication path from application to database file to LTX changesets in object storage

Turso / libSQL

is the managed and edge route. libSQL is an open-contribution fork of SQLite that adds native vector search; Turso layers embedded replicas on top — local reads kept in sync with a remote primary. There's also Turso Database, a ground-up rewrite of SQLite aimed squarely at concurrent writes and async I/O.

Worth knowing about, but it's still in beta as of 2026, so don't build a business on its write-concurrency promises yet.

What Postgres still owns

None of the above turns SQLite into Postgres. Reach for Postgres when you have:

  • High concurrent-write volume — many users writing at once, continuously, not in occasional bursts.
  • Multi-region writes — more than one primary accepting writes.
  • Heavy analytical JOINs across many tables at scale, where the planner and parallel execution matter.
  • Specific extensions — PostGIS for geospatial, pgvector at serious scale, and the rest of the ecosystem.
  • Deep role and permission needs, or a team that's already standardized on Postgres and has the ops muscle for it.

Postgres 18 (released 2025-09-25) also widened its lead on raw performance with a new asynchronous I/O subsystem — up to 3x in certain scenarios, across sequential scans, bitmap heap scans, and vacuum — plus uuidv7() timestamp-ordered UUIDs and planner statistics that survive major-version upgrades. Each major gets a 5-year support window.

Postgres 19 reached Beta 2 on 2026-07-16, with GA expected in September or October, so if you're pinning a version this quarter, pin 18 and plan the 19 upgrade rather than waiting on it.

Cost and ops

This is where small apps feel the difference. SQLite's baseline is a file on a box you're already paying for; the managed Postgres options start where SQLite's ceiling is.

Option Free tier Cheapest paid
SQLite (self-host) $0 — a file on your existing VPS; add Litestream → S3 for backups (storage is pennies/mo) $0 beyond your existing compute
Turso (managed edge SQLite) 5 GB storage, 500M row reads/mo, 10M row writes/mo, 100 DBs Developer $4.99/mo (9 GB)
Neon (serverless Postgres) 0.5 GB storage, 100 CU-hours/mo, autoscale to 2 CU Launch: pay-as-you-go, no monthly minimum · $0.106/CU-hour · $0.35/GB-mo storage
Supabase (Postgres) 500 MB/project, free projects paused after ~1 week idle, max 2 active Pro $25/mo (8 GB)

Two things to watch on the free Postgres tiers. Supabase pauses idle free projects after about a week, so a low-traffic side project can greet visitors with a cold database. Neon's serverless model scales compute to zero, which is cheap but means cold-start latency on the first request after a quiet spell. SQLite has neither problem because there's no separate database server to wake up.

Chart comparing the monthly cost floor of SQLite, Turso, Neon, and Supabase for a small app

A decision checklist

Choose SQLite when the workload is read-heavy, you're on a single node or edge-replicated, you're solo or a small team, ops simplicity matters, and write volume is low or bursty.

Choose Postgres when you have many concurrent writers, multi-region writes, heavy analytical JOINs, a hard dependency on a specific extension, or a team already running it well.

A concrete default

Start a new small, read-heavy app on SQLite plus Litestream — or Turso if you'd rather have managed and edge reads out of the box. Move to Postgres when you hit a write-concurrency wall you've actually measured, not one you've anticipated.

Here's a trigger you can instrument today: log the share of writes that return SQLITE_BUSY and don't clear inside your configured busy_timeout. Zero, or a handful during a known burst, means the single-writer model is still fine.

A share that keeps climbing week over week — and climbs on ordinary traffic, not just your nightly job — is the wall, and it's the signal to move. Pick the threshold that matches your latency budget before you need it, write it down, and let the number make the call instead of the anxiety.

The migration is real work, but it's a known path, and doing it on evidence beats carrying Postgres's operational weight for a year of traffic that never showed up.

One maintenance note for whichever you pick: SQLite has no formal LTS, and the project's advice is to run current (3.53.4 as of 2026-07-24). Version numbers aren't a schedule you can plan around either — 3.52.0 was withdrawn before it ever shipped, and everything in it moved forward into 3.53.0.

Postgres 18 gives you a 5-year window per major, so you can pin and plan upgrades on your own schedule.

References

Related Posts