In-depth articles on technology shaping what comes next.

Distributed SQLite: 5 Myths Tested After 12 Years

After twelve years of distributed SQLite — Raft, Litestream, Turso, D1 — we test five beliefs about consistency, replication, and when to use Postgres.

A ring of small file cabinets connected by threads of light, one glowing as leader
Twelve years of distributed SQLite: many designs, one core idea that keeps being reinvented.

Distributed SQLite went from one person's weekend project to a venture-funded product category without most engineers noticing the decade in between. In September 2014, Philip O'Toole put a Raft log in front of SQLite, replicated every write across a cluster, and pushed the code to GitHub as rqlite. Twelve years later there's a whole ecosystem — dqlite, Litestream, LiteFS, cr-sqlite, libSQL/Turso, Cloudflare D1 — and most of what people believe about it is wrong in interesting ways.

I've run clustered databases in production and cleaned up after their failure modes, so let me save you some pain: the arguments about distributed SQLite are almost never about SQLite. They're about which question you're actually asking — what if the machine dies, what if the machine dies and I can't wait, or what if there is no machine. Here are five myths, tested against twelve years of history.

Myth 1: Distributed SQLite is a recent idea

The current wave of writing about replicated SQLite starts around 2021, when Litestream and the "SQLite is enough" movement got loud, and Turso and Cloudflare D1 arrived with funding and launch posts. So the belief took hold that someone clever recently noticed SQLite could be replicated. In fact the first Raft-over-SQLite system shipped in 2014, and it wasn't even the first replication of SQLite — Expensify had been doing it internally with a Paxos-style protocol for years before releasing Bedrock in 2016, and ActorDB gave every Erlang actor its own Raft-replicated SQLite database around the same era.

Why does this matter beyond credit? Because a design that survives twelve years of independent reinvention tells you something. At least eight different teams, with different needs and different protocols, looked at the problem and landed on the same architecture: SQLite as the storage engine, replication bolted on the outside. Nobody found a reason to stop doing it. When you see that pattern — convergent re-implementation over a decade — you're looking at a genuinely good idea, not a hype cycle. Compare it to categories that flare up and vanish; this one kept compounding.

It also matters because the old design decisions are still load-bearing. rqlite chose to be a CP system in 2014 — writes go through the leader, a majority must acknowledge, a partitioned minority refuses writes — and never changed its mind. That single choice dictated the HTTP API, the read consistency levels, and the snapshotting behavior. Teams that treat consistency as a configuration toggle to be decided later usually regret it; the gap between the spec you implied and the one you implemented is exactly where distributed bugs breed.

Myth 2: Raft on SQLite gives you a cheap distributed Postgres

This is the most expensive myth on the list. The pitch sounds seductive: take the world's most deployed database, add consensus, and skip the operational weight of a client-server system. What you actually get is a single-writer database where every write pays a network round trip to a majority of nodes, and where SQLite's SQL surface — no stored procedures, a thin type system, limited concurrency primitives — is still SQLite's SQL surface.

The write path is the honest cost. A local SQLite commit is a couple of fsyncs. A Raft-replicated commit is: leader appends to its log, ships the entry to followers, waits for a majority to persist, applies to SQLite, then replies. On a healthy LAN that's maybe one or two milliseconds instead of tens of microseconds. Across regions, it's the speed of light plus disk. For read-heavy workloads with modest write rates — configuration stores, service discovery metadata, small control planes — that's fine, and it's exactly the niche rqlite was built for (its origin story is a clustered system needing a small amount of relational storage, with no good answer for which node should hold it). For a hot OLTP workload, it's the wrong tool.

And then there's the operational reality nobody puts in the README: leader election is a failure mode you now own. Split-brain is prevented by majority voting, yes, but that means a three-node cluster that loses two nodes refuses all writes. You've traded "the database is down because the server died" for "the database is down because quorum died," and the second one is harder to explain to your on-call at 3 a.m. If you haven't thought about Jepsen-style testing of your chosen system, you should — one Hacker News commenter asked the pointed question of whether rqlite has been Jepsen-tested, because systems that haven't usually have surprises lurking. Meanwhile, one experienced engineer in the same discussion noted that with modern tooling, running actual Postgres has gotten easy enough that "just use a database server" is a legitimate default again. He's not wrong.

A balance scale weighing a simple block against a tangle of chains and gears
Consensus buys you failover; the price is paid on every single write.

Myth 3: Replication means consensus

If there's one insight the twelve-year history actually delivers, it's this: "replicated SQLite" is not one design, it's three corners of a design space, and they appeared in a telling order. Confusing them is how teams end up running a Raft cluster to solve a backup problem.

  • Consensus (CP). rqlite, dqlite. One leader, strong consistency, a real cluster. Answers: "what if the machine dies and I cannot wait to restore?" Cost: write latency and quorum operations.
  • Streaming (one writer). Litestream, later LiteFS. No consensus at all — the SQLite WAL is streamed to object storage, giving near-continuous disaster recovery for a single node. Answers: "what if the machine dies?" Cost: a small window of possible data loss and restore time measured in seconds to minutes.
  • Merge (AP). cr-sqlite and the local-first movement. CRDTs as a SQLite extension, many writers diverging and merging without a leader. Answers: "what if there is no central machine at all?" Cost: eventual consistency and merge semantics you must design for.

Ben Johnson's argument for Litestream deserves respect because it was correct about most applications: they run on one machine and need a way to recover when it dies, not a cluster. The first question — "what if the machine dies" — is far more common than the second — "and I cannot wait to restore." I've watched teams deploy a three-node Raft cluster to protect a database whose RTO requirement was "sometime before lunch." That's not reliability engineering, that's cargo culting. In a nice closing of the loop, Litestream's WAL-streaming approach ended up inspiring how rqlite itself handles the WAL in v8 — the streaming corner fed back into the consensus corner.

So before you pick a tool, write the question down. Literally. "When node X dies, we need writes to resume within N seconds, and we accept losing the last M seconds of committed data." If M is a few seconds and N is a minute, Litestream-to-S3 is your answer and it will never page you about quorum. If M is zero and N is seconds, you're in consensus territory and you'll pay for it on every write. This is the same discipline as any threat model: name the failure before you buy the defense, the way you'd approach systems that resist catastrophe by deciding upfront which catastrophe you're resisting.

Myth 4: Managed "SQLite at the edge" removes the trade-offs

The money in this category went to the platforms — Turso raised venture funding and was acquired by Supabase; Cloudflare shipped D1 inside its edge network. The pitch is that someone else now handles the hard distributed parts. Sometimes that's true. Sometimes it's marketing, and the dirty secret leaks out: one Cloudflare developer admitted publicly that D1 is essentially a wrapper around a singleton Durable Object exposing its SQLite database, and that you're often better off using Durable Objects storage directly. That's not a distributed database in the consensus sense; it's a single SQLite file with a platform around it. Users running D1 in production report reliability to match.

None of this means the managed options are bad. It means the trade-offs didn't disappear — they moved behind an API where you can't inspect them. When you run rqlite yourself, you know exactly what CP means: three nodes, one leader, majority acks, partitioned minority refuses writes. When you buy "SQLite at the edge," you need to ask the same questions of a black box: where is the writer, what happens on partition, what's my actual durability window? If the vendor's answer is vague, your incident review won't be.

Replication as a service doesn't delete the consistency question. It just moves the answer into someone else's infrastructure, where you can't audit it.

The historical pattern is worth internalizing: the open-source systems (rqlite, dqlite, Litestream) did the idea for eight years before anyone sold it, and the capital flowed to whoever turned the idea into a service with a bill. That's the usual fate of infrastructure. Buying is often the right call — but buy with your eyes open about which of the three corners you're actually getting.

A snow globe with a warm cabin inside, sitting in a foggy field of server racks
Managed edge SQLite: a cozy single database wrapped in a platform you can't see into.

Myth 5: Strong consistency is a free upgrade over backups

People frame the choice as backups (weak, cheap) versus consensus (strong, expensive but strictly better). Not true. Strong consistency through Raft is a different product, not a better version of the same product, and it imposes ongoing taxes that backups never do.

Tax one: throughput. One practitioner who used both noted rqlite gives you "stored in more than one place" guarantees but is much slower than a one-node database — that's the consensus round trip, and it never goes away. Tax two: subtle semantics. Replicate by shipping SQL statements and you inherit determinism problems — functions like random() or datetime('now') can diverge across nodes unless the layer rewrites them, which is why some implementations support only a subset of SQLite SQL. Replicate at the WAL level instead and you dodge that, but you couple yourself to SQLite's on-disk format and page layout. There is no layer placement without a compromise. Tax three: distributed transactions are genuinely hard. One engineer described evolving a design where each statement in a transaction was a separate Raft command — correct-ish but slow — into one where the initiating node runs the whole transaction locally, rolls it back, and ships a single commit command with the entire batch. That's the kind of hard-won lesson you only learn by operating these systems.

Here's a minimal illustration of the write-path difference, using rqlite's HTTP API:

# Local SQLite: one process, one fsync, microseconds.
sqlite3 app.db "INSERT INTO kv VALUES ('flag', 'on')"
# rqlite: this HTTP call hides a Raft round trip —
# leader log append, replication to a majority, then apply.
curl -XPOST 'localhost:4001/db/execute?pretty' \
-H 'Content-Type: application/json' \
-d '["INSERT INTO kv VALUES (?, ?)", "flag", "on"]'
# The guarantee changed: the write now survives the loss
# of any single node. So did the latency budget.

And notice what you did not get: read scaling. Raft replicates the log so followers can take over; it doesn't make followers serve strongly consistent reads by default (rqlite offers read-consistency levels precisely because this is a trade-off). If you came for "distributed" hoping for more read capacity, you wanted read replicas, not consensus.

What twelve years of Raft on SQLite actually teach

The category's history is unusually clean evidence for how infrastructure ideas mature. First someone builds the principled thing for a concrete need (rqlite, from a real clustering problem at a real job). Then others rebuild it for their packaging constraints — Canonical's dqlite is a library you embed because Canonical needed a database inside its products, while rqlite is a server you run beside anyone's product. Then someone argues the common case is smaller than the principled solution assumes (Litestream), gets proven right, and the whole field recalibrates. Finally the platforms wrap it and sell it. If you work in infrastructure long enough you'll see this movie again — LLM agents are relearning distributed systems lessons the same way right now.

My no-nonsense recommendation, after all of it: start with single-node SQLite plus WAL streaming to object storage. It's astonishing how far that gets you, and your failure story is a restore, not a consensus postmortem. Graduate to a Raft-based system only when you can state, in one sentence, the availability requirement that streaming can't meet — and accept the write-latency and quorum-operations bill that comes with it. Reach for Postgres the moment you need its SQL surface or multi-writer throughput. And if none of these fit, the broader embeddable-database world has options too, from embeddable graph databases to the local-first CRDT corner.

The myth-busting summary: distributed SQLite isn't new, isn't a cheap Postgres, isn't one design, isn't made safe by a managed wrapper, and isn't a free upgrade over backups. It's a twelve-year-old, well-mapped design space with three corners, each correct for a different question. The engineers who get burned are the ones who never wrote the question down.