Not an absence of servers
Local-first is sometimes read as “no backend”. The Ink & Switch essay that defined the idea says the opposite. The key difference between traditional systems and local-first ones, it argues, is not an absence of servers but a change in their responsibilities: servers are in a supporting role, not the source of truth.
The essay calls these supporting servers cloud peers: servers that help client apps without being on the critical path. An app that cannot open a document, save an edit, or show its data until a server answers has put the server back on the critical path. An app that can do all of that with the network off, and uses the server when it is there, has kept it in a supporting role.
What a backend can do for a local-first app
Relay between devices that are not online together. If one person shares a document and closes their laptop before the other person connects, direct peer-to-peer sync has nobody to sync with. The essay’s suggested fix is a cloud peer that stores a copy of the document and forwards it to other peers when they come online. Automerge’s project publishes a small sync server for exactly this role: it stores documents in a data directory and relays them over WebSocket. Its README describes it as an unsecured Express app, so an app that keeps private data on such a server has to add its own access control.
Keep backups and archives. A server copy protects against a lost or broken device, and the essay notes this is especially useful for phones and other devices with limited storage.
Bridge to other services. The essay’s examples include a bridge to traditional server APIs, such as weather forecasts or stock tickers, and a provider of burst computing for heavy jobs like rendering video on a GPU.
Act as the authority for rules that need one. Merging guarantees that copies converge, not that the result is valid. Two people can each book the last slot offline, and both copies will merge cleanly into a state that breaks the rule. A backend that checks such rules when changes arrive, and rejects one of them, is a straightforward way to hold the line. How sync conflicts are resolved covers when to hand a decision to an authority.
Stay the system of record for everything else. Local-first documents can cover the work people do together in the field while existing databases keep payroll, billing, inventory and history.
Keeping the backend off the critical path
Three design choices keep a backend helpful without making it required.
Use a data model that does not care about the route. Automerge describes itself as network-agnostic: it works over client/server connections such as WebSocket, peer-to-peer connections such as WebRTC, or entirely local links such as Bluetooth, and an Automerge file can even travel as an email attachment or on a USB drive and still merge. When the data merges the same way whatever carried it, the server becomes one route among several. Ditto’s documentation makes the same point for its product: sync works the same whether a device is syncing with other devices in the mesh or with its server. Peer-to-peer sync vs cloud sync compares the routes.
Treat the server’s copy as one replica. The server can be a peer that receives and merges changes like any device. Compare a server-authoritative design: PowerSync’s documentation describes a backend endpoint that applies client writes to the source database, and changes the server does not confirm are removed from the client. That is an offline-first design: in it the server’s copy decides, so it is not local-first in the Ink & Switch sense.
Be clear about what each handoff means. When local work reaches a backend, “sent”, “received” and “committed” are different events, and a device should keep its record until the backend has durably accepted it. Delivered, accepted, committed explains the distinction. Offline Protocol’s backend delivery guide applies this to records retained on devices: it asks for stable event IDs, an adapter that maps events into the destination’s schema with idempotency keys or duplicate-safe writes, and a record of the destination’s durable acceptance for each event ID. It warns against clearing the source merely because the SDK reported delivery.
A quick test
To check whether a backend is in a supporting role, turn it off and use the app:
- Can a person open their data, edit it, and see their edits after a restart?
- Can two people nearby still share changes, if the app supports that?
- When the server comes back, do the copies converge without anyone re-entering work?
- Are the rules that need an authority clearly marked as waiting, rather than silently assumed to have passed?
If the answers are yes, the app has a backend and is still local-first.