Remember when saying “we use SQLite in production” would get you laughed out of a system design interview? That era is definitively over. The past two years have seen an explosion of production-grade tools built on top of SQLite, and the results are compelling enough that we migrated one of our services from Postgres — with dramatic results.
The New SQLite Ecosystem
Let me lay out what’s happened:
- Turso — A distributed SQLite platform built on their libSQL fork. They’ve added features that SQLite core has historically refused:
ALTER COLUMN, native replication across regions, and recently, vector search support. You get a SQLite-compatible database that replicates globally with edge read replicas. - LiteFS — Fly.io’s answer to SQLite replication. It uses FUSE to intercept filesystem operations on the SQLite file and replicate WAL frames to other nodes. Your app thinks it’s writing to a normal SQLite file, but the data is being replicated across Fly’s edge network.
- Cloudflare D1 — SQLite running on Cloudflare’s edge network. This one is significant because it answers the question “how do you put a database on every edge node?” You can’t run Postgres on 300+ edge locations, but you can absolutely run SQLite.
- Litestream — Streaming replication of SQLite to S3, Azure Blob, GCS, or SFTP. It continuously streams WAL changes to object storage, giving you point-in-time recovery without any application changes.
Why the Numbers Are Compelling
The performance characteristics of SQLite, when colocated with your application, are genuinely hard to beat:
- 100K+ read queries per second on modern hardware. This isn’t a benchmark number — it’s what you see in production with a well-indexed database.
- Sub-millisecond read latency. No network hop means your database queries are essentially memory operations (assuming the working set fits in the OS page cache). Compare this to Postgres where even on the same machine you’re paying ~1ms for the Unix socket roundtrip.
- Zero connection management. No connection pools, no
max_connectionstuning, no “too many connections” errors during traffic spikes, no pgBouncer configuration. The database is a function call, not a network service.
The DHA (Durable Hosting Architecture) concept from the SQLite team captures this well: instead of separating your application server and database server, you colocate them. The database file lives on the same machine as the app. Reads are local I/O. Writes go to a local file that’s replicated asynchronously for durability.
Our Migration Story
We migrated a read-heavy API service from Postgres (hosted on RDS) to Turso last quarter. The service handles product catalog queries — lots of reads, infrequent writes, data that changes maybe a few hundred times per day.
The results:
| Metric | Postgres (RDS) | Turso (SQLite) |
|---|---|---|
| P50 latency | 12ms | 0.8ms |
| P99 latency | 45ms | 3ms |
| Monthly infra cost | $340 | $95 |
| Connection errors/week | ~15 | 0 |
| Deployment complexity | High (migrations, connection strings, secrets) | Low (embedded database, single binary) |
The 70% cost reduction came from eliminating the RDS instance entirely. The latency improvement came from removing the network hop — the SQLite database is literally embedded in the application process.
But the biggest win was operational simplicity. No more RDS maintenance windows. No more connection pool debugging at 2 AM. No more “the database is in us-east-1 but the app is in us-west-2” latency discussions. The database goes where the app goes.
Where SQLite Still Falls Short
I want to be honest about the limitations, because I’ve seen too many “SQLite is all you need” takes that gloss over real constraints:
- Write throughput is limited. SQLite’s WAL mode allows concurrent reads with a single writer, but writes are still serialized. If you need more than ~1,000 write transactions per second, you’ll hit contention. For write-heavy workloads (event logging, real-time analytics ingestion, high-traffic social feeds), Postgres or a purpose-built solution is still the right choice.
- No cross-database joins. If your architecture involves multiple services that need to query each other’s data, you can’t do a cross-database join in SQLite. You need application-level orchestration.
- Immature monitoring tooling. The ecosystem of monitoring, alerting, and observability tools for SQLite in production is thin compared to Postgres. No pganalyze equivalent, no mature query performance monitoring, limited slow query logging.
- The type system is permissive. SQLite’s dynamic typing means it’ll happily store a string in an INTEGER column. If you’re coming from Postgres with strict typing, you need to add application-level validation or use strict mode (added in SQLite 3.37).
The Question
Is SQLite-in-production a real paradigm shift for how we build web applications, or does it only make sense for a specific slice of use cases — read-heavy, moderate data size, single-region deployments?
I’m particularly curious to hear from anyone running SQLite at scale with Turso or LiteFS. How’s the multi-region replication story in practice? And for the data engineers — how are you thinking about SQLite as an analytical store alongside tools like DuckDB?