Contents

Local-First Software Is Quietly Winning

Contents

The app I use for notes works perfectly on a train with no signal, on a plane in flight mode, and during the semi-regular occasions my home broadband drops out for an afternoon. It also syncs across every device I own within seconds of a connection coming back. Neither property is remarkable on its own — offline support and sync both exist elsewhere — but the combination, and specifically which one is the default and which is the enhancement, is the entire argument for what “local-first” software actually means, and why it is a meaningfully different architecture from the cloud-native default most software has quietly assumed for the last fifteen years.

What cloud-native actually assumes, and what breaks when it’s wrong

Advertisement

Most software built in the last decade and a half assumes a network connection is the normal state and its absence is the exception to handle, usually badly. Open the app without connectivity and you get a spinner, an error, or in the better cases a stale cached view you cannot meaningfully interact with — no drafting a note, no marking a task done, because the source of truth lives on a server you cannot currently reach, and the local device is just a thin window onto it. That architecture makes sense for a genuinely collaborative real-time document that several people are editing simultaneously, where a server has to be the arbiter of ordering. It makes considerably less sense for a personal notes app, a to-do list, or a budget tracker, where the entire dataset belongs to one person and the network is doing nothing except getting in the way of an interaction that could have been purely local.

Local-first inverts the assumption: the local copy on your device is the primary copy, every read and write happens against it instantly regardless of connectivity, and synchronisation across devices is a background process that reconciles copies when a network happens to be available, rather than a precondition for the app to function at all. The user-facing difference is enormous even though the underlying data model can be similar — an app built local-first is, by construction, never blocked on the network for its core interaction loop.

CRDTs are the part that makes this actually work

The hard technical problem local-first software has to solve is what happens when the same document gets edited on two devices while they are both offline, then reconnects. A naive last-write-wins merge silently discards one set of changes, which is exactly the data-loss failure mode that made people distrust “offline mode” features in older software. Conflict-free Replicated Data Types (CRDTs) are the structure that solves this properly: a CRDT is a data structure specifically designed so that any two independently modified copies can be merged automatically, deterministically, and without data loss, because every operation is expressed in a form that commutes — applying two changes in either order produces the same final result.

A simple example makes the idea concrete: a plain text field where two devices each append a character while offline is genuinely ambiguous to merge correctly, because “insert at position 4” means something different depending on what else has already been inserted at earlier positions on the other device. A CRDT-based text structure instead assigns every character a stable, globally unique identifier tied to a causal position relative to its neighbours rather than a plain numeric offset, so merging two divergent edit histories can always determine a single consistent ordering without needing either device to have seen the other’s changes first. This is genuinely different from “conflict resolution” as most software understands it — there is no conflict to resolve because the data structure was designed so conflicts cannot arise from concurrent edits in the first place, only from two devices editing the exact same character, which is a narrower and much more tractable problem.

The self-hosted software already doing this well

Advertisement

A fair amount of the self-hosted ecosystem has been quietly building local-first-shaped tools without necessarily using the term. Actual Budget keeps your entire ledger as a local SQLite-backed store on-device and syncs changes through a server you can self-host, rather than the server being the authoritative store the client merely displays — which means the app remains fully usable, entering transactions and checking balances, through a connectivity gap that would leave a cloud-native banking app entirely unusable until the connection returns. Vikunja and similar task managers increasingly offer the same shape, with genuine offline task creation and editing rather than a read-only cached view.

The pattern extends past obviously document-shaped apps too. A local RAG stack for chatting with your own documents is local-first in spirit even though it is not solving a multi-device sync problem at all — the point is that the entire interaction, embedding generation and retrieval included, happens against data and a model that never leave your machine, which is the same underlying value (your data’s availability and usefulness does not depend on a remote service’s uptime or continued existence) applied to a different kind of workload. And the database engine underneath a lot of this software is frequently SQLite itself, precisely because a single portable local file is the natural storage substrate for an architecture that treats the device as the primary copy rather than a cache of one.

Where local-first genuinely struggles

Real-time collaborative editing among many simultaneous participants — think a shared spreadsheet several people are actively typing into at once — is the case where local-first’s advantages compress and its costs remain. CRDTs handle this in principle, but the metadata overhead of tracking causal ordering for every individual edit scales with the number of concurrent editors and the churn rate of changes, and at a large enough scale that overhead becomes a genuine performance cost rather than a rounding error. For that specific shape of workload — many people, constant concurrent edits, a genuine need for a canonical server-mediated order — a traditional client-server architecture with the server as arbiter remains the more practical choice, and pretending otherwise for ideological reasons is not a good trade.

Search across very large datasets is the other honest limitation. A local-first note-taking app with tens of thousands of notes needs its full-text search index to also live locally, which is a solved problem at that scale, but the same architecture applied to something genuinely large — searching millions of records — starts to strain a single device’s storage and compute in a way a centralised server with proper indexing infrastructure would not.

Why it took this long to become practical

Local-first is not a new idea — desktop software before broadband was ubiquitous was local-first by default, simply because there was no reliable alternative. What changed is that cloud-native became the default for reasons that had nothing to do with users’ actual needs: recurring subscription revenue is easier to justify when the data lives on a server you control access to, and centralised telemetry is easier to collect when every interaction already round-trips through your infrastructure. CRDTs existed in academic research well before local-first software became practical to build, but building a genuinely reliable implementation, with the causal-ordering metadata efficient enough not to bloat storage unreasonably, took years of engineering the research papers did not need to solve.

That history matters because it reframes what changed. It is not that offline support suddenly became technically possible — basic offline caching has existed for decades in various half-working forms. What changed is that a properly principled way of reconciling concurrent offline edits without data loss became mature enough to ship in consumer software, rather than remaining a research curiosity or a set of ad hoc heuristics that occasionally ate someone’s changes without explanation.

The self-hosting angle specifically

Local-first and self-hosted are two separate axes that happen to compound well together, and the self-hosted homelab is arguably the most natural home for local-first software’s syncing half. A local-first app still typically needs some server to relay changes between your own devices when they are not on the same network — your phone and your laptop are not usually within Bluetooth or LAN range of each other, so something has to carry the CRDT operation log between them over the internet. Self-hosting that relay server yourself, rather than trusting the app vendor’s, closes the last gap in the durability argument: your data’s availability now depends on neither a vendor’s servers nor your own device’s survival alone, but on infrastructure you actually control and can back up on your own terms. Actual Budget follows roughly this shape for personal finance specifically — a local-first client paired with a sync server you run yourself, rather than a vendor’s — and it sits usefully alongside more traditional self-hosted ledgers like Firefly III, which takes the more conventional server-as-source-of-truth approach and is worth comparing against if you want the fuller picture of what “self-hosted finance” actually spans. The combination of a local-first client and a self-hosted sync relay is close to the strongest data-ownership position available to an individual today without giving up any of the convenience that made cloud software appealing in the first place.

Troubleshooting sync conflicts when they do happen

Even with CRDTs, users occasionally hit a case that looks like a lost edit, and it is worth understanding the actual mechanism before assuming the sync engine is broken. The most common cause is two devices editing the same field of the same record simultaneously while both offline — genuinely the same character position in the same document, not merely the same document at different positions — where even a correct CRDT implementation has to pick one resolution deterministically, and the “losing” edit is not silently dropped so much as superseded by a rule (often, whichever edit has a later logical timestamp in the CRDT’s own causal ordering, not wall-clock time) that a user has no visibility into unless the app surfaces it.

A device that appears permanently “stuck” out of sync, showing a different state than every other device, is usually either a genuinely broken network path that the app is not surfacing clearly as an error, or a sync engine that has accumulated an operation log large enough that a full replay on reconnect is taking far longer than the UI’s patience implies progress is happening. Checking the sync engine’s own logs for a stalled or repeatedly failing replay, rather than assuming data loss, is worth doing before restoring from a backup — most “stuck” states resolve once the actual underlying network or storage issue is fixed, since the operation log itself is usually still intact and simply waiting to finish applying.

A device that rejoined after a very long offline period and appears to have “lost” changes made just before it went offline deserves a specific check: some local-first implementations bound how far back their operation log retains individual changes, compacting older history into a snapshot for storage efficiency, and a device offline longer than that retention window can end up needing a full state resync rather than an incremental replay. This is a real trade-off in the implementation, not a bug, and it is worth knowing your specific tool’s retention window before assuming an extended offline period is risk-free.

Is the model worth choosing deliberately

For personal, single-owner data — notes, budgets, tasks, a document only you or your household actually edit — yes, and it is worth actively seeking out software built this way rather than defaulting to whatever cloud-native option is most heavily marketed. The payoff is not just resilience to a bad connection, though that alone is worth having; it is that your data’s usefulness stops being contingent on a company’s servers, business model or continued existence, which is a genuinely different kind of durability from anything a cloud-native app can offer by design. For genuinely collaborative, many-editor, real-time workloads, the honest answer is that a server-mediated architecture still wins, and no amount of local-first enthusiasm changes that. Knowing which category your own use case actually falls into, rather than assuming one architecture fits everything, is most of the judgement call.

Advertisement
Advertisement
Smarc
Written by Smarc

Founder and editor of vo.rs. A lifelong tinkerer who self-hosts far more than is sensible, hardens Linux boxes for fun, and prods the latest AI tools to see what they can really do. The how-to guides here are the notes Smarc wishes had existed the first time round.