Durable Objects and Strongly Consistent State at the Edge
Durable Objects pair compute with storage to eliminate edge latency without sacrificing consistency.

Edge compute made a promise: put the server close to the user, and latency disappears. It kept that promise for anything stateless. The moment an application needs to remember something between requests, the promise runs into a wall, because state that lives in one place is state that everyone else has to ask permission to touch. Durable Objects, a primitive shipped in 2020, close that gap by fusing single-threaded compute with its own transactional storage, so a given slice of state has exactly one authoritative owner no matter how many regions are hitting it. That fusion is the whole story. Everything else, the actor model lineage, the SQLite backend, the AI agent use case, is a consequence of that one design choice.
Why edge compute and consistency fight each other
The standard pitch for edge compute goes like this: route the request to the nearest node, latency drops, throughput climbs, everyone's happy. Fine, as far as it goes. But the moment two nodes hold their own copy of the same piece of state, someone has to keep those copies in agreement, and that coordination work eats back a chunk of the latency you just won. This is the old distributed systems trade-off in a new outfit: distribute for speed, centralize for consistency, and you generally get to pick two.
Eventual consistency is the industry's default workaround, and it's worth being honest about what "eventual" actually means. Key-value stores built for global replication can take a noticeable amount of time to propagate an update everywhere. That's a shrug for a feature flag or a cached API response. It's a liability the moment two users are writing to the same counter, session, or document at the same time. Ask anyone who's tried to build an atomic increment on a shared cache: you end up reaching for Lua scripts, or WATCH/MULTI/EXEC, or optimistic locking with retries, just to stop two writers from stepping on each other. None of that complexity is optional. It's the tax eventual consistency charges you at the application layer, and it shows up in every service that touches the counter.
Real-time collaboration makes the problem impossible to ignore. Two people editing the same document from two different edge nodes, with no coordination layer between them, produce conflicting writes, full stop. Fixing that after the fact means CRDTs or operational transformation, both of which are legitimate tools and both of which exist only because the state wasn't coordinated at the source. The question hanging over the industry before 2020 was blunt: how do you get strong consistency without funneling every request back through one origin server and eating the round-trip latency that defeats the point of edge compute in the first place?
What Durable Objects are and how the actor model produces consistency
A Durable Object is a Worker with a permanent, globally unique identity and its own private, strongly consistent storage. Compute and storage aren't two systems talking to each other here; they're one unit.
The lineage traces back to Carl Hewitt's actor model, formalized at MIT in 1973. Instead of shared memory protected by locks, each actor owns its state outright and only talks to the outside world through messages. Erlang ran with this for telecom systems that couldn't afford to go down. Microsoft built Orleans on the same idea to run Xbox-scale services. Akka carried it into finance, retail, and gaming. What none of those systems gave you for free was global uniqueness backed by storage that just works, without a team standing up Cassandra or Zookeeper behind the scenes. That's the piece Durable Objects add.
A database row is passive: it holds data, but it needs some external process to come along and act on it, and that process has to be careful about who else might be acting on it at the same time. A Durable Object is active. It has its own thread. Requests addressed to the same object get processed one at a time, in order, which means there's no mutex to forget to lock, because there's only ever one thread touching that object's state. Name an object "user-123" and every request for it, from Tokyo, from Toronto, from Tallinn, lands on the same instance. The routing is automatic and it's addressed by name, not by region.
Because the storage lives inside the object instead of across a network call, reads and writes are strongly consistent without the latency penalty you'd expect from "strongly consistent" anything. And the atomicity is often implicit: a run of write operations with no await in between gets committed as a unit automatically, and a read immediately followed by a write behaves like a transaction even if nobody wrote a BEGIN. The system also blocks concurrent events from running while a read is in flight, unless a developer explicitly opts into allowConcurrency: true. Consistency here is the default, not a setting you have to remember to turn on.
Stop counting instances, start counting entities
Developers coming from other platforms bring an instinct that's actively wrong here: count your instances, provision for peak, plan your sharding strategy before you write a line of business logic. Applied to Durable Objects, that instinct produces bad architecture, because trying to minimize instance count forces you to cram unrelated data into shared objects, and that reintroduces exactly the coordination headache Durable Objects exist to remove.
The correct reframe is simpler than the instinct it replaces: one object per logical entity. One per user. One per document. One per game session. One per chat room. Think of each object as a database row that happens to run code: lightweight, created on demand, cleaned up automatically once nobody needs it. Spinning up a million of them isn't a scaling event, it's Tuesday.
Let the domain model decide the count, not a capacity spreadsheet. A collaborative editor gets one object per document. A multiplayer game gets one per session, one per player, one per match. Each object caps out at 10 GB of durable storage, but for most real applications the storage ceiling matters less than the coordination guarantee. The platform handles placement and lifecycle on its own; the application just spreads work across as many named objects as the domain calls for. When the question in your head shifts from "how many Durable Objects will this cost me" to "what logical entity does this request belong to," the mental model has actually landed.
The storage layer: key-value roots, SQLite now standard
Durable Objects started with a transactional key-value API: put, get, list, delete, private to that one object instance and invisible to any other. It supports atomic multi-key writes (all of them land or none do), prefix-based listing, and strong consistency, all scoped to the object.
In 2024, a SQLite storage backend arrived, bringing real relational features (joins, indexes, arbitrary SQL) to a primitive that used to be key-value only. It's worth separating this from D1, a managed SQL database, because they solve different problems. D1 fits the traditional shape: stateless application servers on one side, a database over the network on the other. SQLite-in-DO collapses that gap; the query runs inside the same object that owns the data, no network hop between the two. D1, in fact, is built on top of SQLite-in-Durable-Objects, so the relationship isn't a comparison between competitors, it's a layering. D1 adds a ready-made HTTP API and managed observability like query insights; SQLite-in-DO trades that convenience for lower-level control. The SQLite backend is recommended for every new Durable Object class going forward.
Beyond SQL, the storage API includes additional scheduling and durability controls. Objects can also cache hot data in memory between requests as a speed layer, but that memory resets on hibernation, so anything that needs to survive an idle period has to actually be written to durable storage, not just held in RAM. Billing for SQLite-backed storage doesn't start before January 7, 2026. Once it does, the Workers Free plan includes roughly 150 million rows read and 3 million rows written per month with 5 GB of storage, no charge. The Workers Paid plan includes 25 billion rows read and 50 million rows written per month, also with 5 GB before overage rates apply.
When Durable Objects are the right call, and when they're overkill
One question settles most of the debate: do concurrent requests to this entity need to see each other's effects immediately? If yes, reach for Durable Objects. If no, D1 or a key-value store will do the job for less money and less complexity.
KV fits read-heavy workloads where eventual consistency is fine and the access pattern is a simple lookup: configuration values, feature flags, cached responses. D1 fits when you need relational queries and a more managed database experience with built-in observability, and your consistency needs aren't extreme. Durable Objects earn their keep on rate limiting (one object per limited resource, where serial execution makes atomic increments trivial and Lua scripts unnecessary), document collaboration (every edit routes to one object and gets serialized for free), session management, distributed locks, and multiplayer game state.
Clerk's authentication infrastructure is a clean case study in "required," not "preferred." KV's total lack of a strong consistency guarantee disqualified it outright for auth-critical paths, where a stale read isn't an inconvenience, it's a security gap. Durable Objects were the only primitive on the table that actually met the bar.
WebSocket-based sessions are a similarly natural fit: long-lived connections need one stable place to coordinate, and a Durable Object can hold that session, coordinate every connected client, and persist state without any extra infrastructure bolted on. On billing, incoming WebSocket messages are metered at a 20:1 ratio (twenty messages count as one billed request), which matters when estimating cost for anything chatty.
None of this replaces a general-purpose relational database reachable from arbitrary compute, and it's not a substitute for caching layers built around rich data structures like sorted sets or streams, with the broad client ecosystems those tools carry. The honest trade is portability and ecosystem breadth on one side, versus built-in consistency and less operational overhead on the other.
Where real-time products actually stress-tested the model
Gaming, financial services, collaborative tools, and real-time APIs are where this primitive has taken its hardest hits and kept working. Vaultrice built the foundation of its stateful edge API on Durable Objects specifically because single-threaded execution eliminates the race conditions that eventually-consistent systems are prone to; every operation on a given data object runs in sequence, not in a scramble.
Liveblocks has pointed to the staffing math directly: running WebSocket infrastructure without a managed primitive like this might have required at least four additional hires just to keep it operational. The primitive absorbed work that would otherwise need a standing team.
Chat rooms and multiplayer games follow the same shape. One Durable Object instance coordinates every participant, and the platform routes every client to that same instance regardless of where in the world they're connecting from, so multiplayer state stays consistent without any application-level locking. Financial services fit the pattern too: the actor model has a track record in finance through systems like Akka, and Durable Objects extend that same discipline to the edge with automatic durability built in, which matters for latency-sensitive operations that can't tolerate a "the balance will update eventually" answer. The thread running through all of these: the object is the final word on its slice of state. There's no reconciliation pass, no conflict resolution step, no waiting for the rest of the system to catch up.
Why AI agents need the same guarantees, and usually don't have them
An agent that loses track of its own state between steps, or runs two reasoning steps at once against a stale context, doesn't fail gracefully. It takes the wrong action, or it takes the right action twice. Eventual consistency, tolerable for a cached banner ad, is not tolerable for a system deciding whether to refund a customer or fire off a trade.
Production agents need seven things a weekend demo skips entirely: durable state, error recovery, observability, cost control, a human-in-the-loop checkpoint, tool security, and a bounded scope of action. Each of those maps cleanly onto a platform primitive rather than a pile of custom glue code. Making each agent its own addressable Durable Object gets you embedded SQL for memory, single-threaded execution for coordination, and global uniqueness so any client, anywhere, reaches that same agent instance instead of a random clone of it.
An Agents SDK builds directly on this: agents are Durable Objects, each one a small standalone server holding onto context across interactions, capable of remembering a user's past preferences and adjusting behavior based on what happened last time. That effort has also grown four related primitives worth naming. Dynamic Workers give AI-generated code an isolate-based runtime with cold starts reported to be dramatically faster than traditional containers, letting a generated JavaScript snippet for an API call or a data transform spin up in milliseconds and vanish. Artifacts provide Git-compatible storage built to hold tens of millions of repositories. Sandboxes offer persistent Linux environments for builds or package installs that take longer than a single request. And a "Think" framework adds durable state so an agent can hold onto a multi-step plan instead of forgetting it mid-execution. Sitting above all of this, Workflows, now generally available, handle multi-step applications that retry automatically, persist their state, and run for minutes or weeks at a stretch, acting as the durable execution layer built on top of the object's own storage.
None of this is happening in a vacuum where every agent project succeeds by default. Industry observers have cautioned that a substantial share of agentic AI projects may be abandoned, given concerns about costs, unclear payoff, and risk controls. The infrastructure argument for building agents on Durable Objects is, in part, a direct rebuttal to that failure pattern: cheaper to run, and harder to get subtly wrong.
The shape of the pattern, once it clicks
Strip away the specific use cases and a single architecture is doing all the work. Workers sit at the edge handling routing and anything stateless. Any request that touches actual state gets addressed to a named Durable Object. That object's single thread and colocated SQLite storage make every operation on it consistent, with no distributed lock, no consensus protocol, and no conflict resolution code anywhere in the application.
Workers handle the routing side of this without ceremony: every request for a given Durable Object lands on the same instance, because the application names the logical entity and the platform figures out geography and placement on its own. Communication between Workers and Durable Objects happens over RPC, using ordinary JavaScript methods and objects rather than HTTP round-trips, which is both simpler to reason about and more efficient than pushing everything through a message queue.
Objects stay alive while they're handling requests, hibernate once things go quiet, and wake back up on demand, which means there's no charge for an idle server sitting around waiting for traffic. In-memory state is there to make hot paths faster, not something the reliability of the system depends on. The alarm API turns scheduled work, like periodic aggregation or a deferred follow-up action, into something the object handles by waking itself, with no external cron service required. And the identifier itself, "user-123," is a coordination address rather than a server location: it means the same thing from any client, in any region, at any hour, and always resolves back to the one authoritative copy of that state.
The pattern has edges worth naming plainly. Entities that genuinely need relational queries spanning many objects at once belong in D1, not crammed into a single Durable Object's storage. Workloads that would require transactions spanning multiple objects, or that would blow past sensible per-object storage limits, are a sign to rethink the boundary rather than force the primitive to do something it isn't built for. Knowing where the pattern stops is part of what makes it credible in the first place. What's left, once the boundaries are respected, is a rare combination: strong consistency without a centralized origin server, low latency without giving up coordination, and horizontal scale without hand-rolled sharding. Those three used to fight each other. Here, because compute and storage never separate in the first place, they don't have to.
Sources
- What are Durable Objects?
- Chapter 6: Durable Objects: Stateful Compute at the Edge | Architecting on Cloudflare
- Maintaining consistent state. Cloudflare durable objects maintain… | by Larry Maccherone | Cloudflare Durable Objects Design Patterns | Medium
- Durable Objects (DO) — Unlimited single-threaded servers spread across the world | Lambros Petrou
- developers.cloudflare.com
- blog.cloudflare.com
- cloudflare.com
- blog.cloudflare.com


