O'Toole uses the 12-year retrospective to document — in unusual detail — the places rqlite's design was wrong and had to be reworked: snapshotting, on-disk format, write serialization through the leader, and the dozen subtle ways SQLite's single-writer assumption fights every distributed-systems instinct. He frames the retrospective as a field report on specific compromises rather than a victory lap, arguing the interesting lesson isn't 'Raft is hard' but how SQLite's architecture resists being
O'Toole explicitly calls out rqlite's deliberate ceiling: it is not a sharded, horizontally-scaling OLTP database and never will be. He positions this constraint as a feature, arguing rqlite is purpose-built for configuration, metadata, and small-to-medium workloads where SQLite's simplicity plus Raft's durability is the right trade — and that trying to be more would have killed the project.
Philip O'Toole published a retrospective marking twelve years of work on rqlite, the distributed, fault-tolerant database he built on top of SQLite and the Raft consensus algorithm. The project started in August 2014 as a weekend experiment — O'Toole wanted to teach himself Go and Raft, and SQLite was the smallest embeddable SQL engine he could wrap. Twelve years later, rqlite has shipped eight major releases, accumulated 15k+ GitHub stars, and quietly become one of the longest-running distributed-SQLite projects in production.
The post is not a victory lap. O'Toole catalogs, in unusual detail, the places the design was wrong and had to be reworked — snapshotting, the on-disk format, how writes are serialized through the leader, and the dozen subtle ways SQLite's single-writer assumption fights every distributed-systems instinct you have. He also calls out the project's deliberate ceiling: rqlite is not a sharded, horizontally-scaling OLTP database and never will be. It's a replicated SQL store for configuration, metadata, and small-to-medium workloads where you want SQLite's simplicity and Raft's durability — not a Postgres replacement.
The timing matters. The distributed-SQLite space has gotten crowded fast: Canonical's Dqlite (powering LXD and MicroK8s), Fly.io's LiteFS (shipped 2022, deprecated 2024 in favor of their newer SQLite replication), Turso's libSQL fork, Cloudflare's D1, and a handful of smaller projects. O'Toole's retrospective lands as the field is sorting itself into camps, and it's the closest thing we have to a long-form field report from someone who's maintained one of these systems through every design mistake.
The interesting content isn't "Raft is hard" — everyone who's read the Raft paper knows that. The interesting content is how SQLite's design actively resists being distributed, and the specific compromises you make when you force it anyway. SQLite is a single-writer, single-process, file-backed engine. Its performance story is built around the assumption that the process holding the write lock is the one executing the SQL. The moment you put a Raft log in front of it, every write has to be serialized to a byte stream, replicated, and then re-executed on each follower — which means SQL that isn't deterministic (think `RANDOM()`, `CURRENT_TIMESTAMP`, triggers calling user functions) becomes a correctness bug. rqlite's answer, after years of iteration, was to rewrite SQL on the leader into its deterministic form before it hits the log. That's a nontrivial amount of parsing work for something SQLite users assume is free.
Snapshotting is the other place where theory meets grind. Raft requires periodic snapshots so the log doesn't grow forever, but SQLite's "snapshot" is an entire database file. Early rqlite copied the whole file each time, which was fine until users had multi-gigabyte databases. The current design uses SQLite's own online backup API plus WAL truncation to produce consistent snapshots without blocking writers — a solution that took years to converge on and that is still, in O'Toole's words, the most fragile part of the system.
Compare the approaches the field has settled on and the trade-offs become clearer. Dqlite replaces SQLite's VFS layer to replicate writes at the page level — faster, but tightly coupled to a forked SQLite build. LiteFS replicated at the filesystem layer via FUSE, which was elegant until it wasn't (Fly.io shut it down after discovering edge cases they couldn't close). Turso's libSQL forked SQLite outright to add server-side replication primitives. rqlite replicates at the SQL-statement layer, which is the slowest option but also the only one where the underlying SQLite is unmodified upstream. Each team made a different trade between performance and the long-term cost of carrying a fork.
The HN discussion (136 points at time of writing) is where the honest reactions live. Several commenters who evaluated rqlite in production converged on the same conclusion: it's the right tool when your workload is "small amount of critical state replicated across 3-5 nodes" and the wrong tool for anything that looks like a transactional app database. That's a narrower niche than it sounds, but it's a real one — Kubernetes-style control planes, feature-flag stores, config services, and the metadata layer behind a bunch of SaaS products.
If you're considering distributed SQLite for a new project, the retrospective is a decision aid. Match the project to your actual access pattern: rqlite for config/metadata where you want SQL and HA but write volume is modest; Dqlite if you're already in the Canonical ecosystem; libSQL/Turso if you want hosted and are okay with a forked engine; D1 if you're on Cloudflare Workers and your reads dominate. Don't pick one because it's trendy — pick based on whether you can live with the specific failure mode it has.
The deeper lesson for anyone building infrastructure is about the gap between a correct algorithm and a maintainable system. Raft the paper is 18 pages. rqlite the implementation is twelve years of patching things the paper doesn't discuss: how to do log compaction without pausing writes, how to handle a node that comes back after a six-week outage with a stale snapshot, how to let operators force-promote a replica when quorum is lost without silently corrupting data. None of these are research problems. They're the operational tail that determines whether your database is actually usable in year five.
For senior devs evaluating any "distributed X" project, the useful question is: how long has one person been patching the operational edges? A Raft implementation with 10k lines of code and six months of history is a demo. One with 50k lines, a changelog going back a decade, and a maintainer who can name every regression — that's a database.
O'Toole hints that rqlite 9 will focus on read scalability and better observability, not new consensus work — a sign that the hard core of the project has settled. The broader distributed-SQLite space is still shaking out: LiteFS is gone, Turso is pivoting toward their own storage engine, and Cloudflare D1 is becoming the default for a lot of edge workloads. rqlite's niche — a boring, un-forked, Go-native replicated SQL store — is likely to stay useful for exactly the reason it's lasted twelve years: it does one thing, it's been debugged in anger, and the maintainer is still here.
Top 10 dev stories every morning at 8am UTC. AI-curated. Retro terminal HTML email.