Skip to main content

Durable Objects Just Escaped Cloudflare Twice: Deno's Celld vs Rivet Actors

12 min readDora NodaDora Noda
Share
On this page

Cloudflare built the best stateful primitive in serverless — a single-writer object with its own SQLite database, compute colocated next to the data — and then made it available in exactly one place: Cloudflare. As one 2026 analysis put it bluntly, "D1 does not port anywhere. Durable Objects do not port anywhere. If Cloudflare changes its pricing badly, you are on a migration project." That sentence is the whole reason the actor model keeps trying to escape the walled garden. In August 2026 it escaped again: Deno's Ryan Dahl unveiled celld, "a self-hosted, distributed Durable Objects and Workers implementation," eighteen months after Rivet Actors became the first serious open-source exit. Two escape hatches now exist. They solve the same problem in radically different ways — and comparing them tells you exactly what a platform has to provide before tenants can run actor workloads next to their stateless services.

Here is the comparison up front, with the details behind each row in the sections below.

Deno celldRivet Actors
Programming modelCloudflare Workers + Durable Objects APIs (Wrangler bundles run as-is)Own TypeScript actor framework (@rivet-gg/actor: RPC, state, events)
Runtime / isolationEvery node embeds V8, executes your Worker bundleRust runtime with isolates; supports the Deno runtime, so npm and JSR packages work
State backendOne SQLite database per cell, continuously replicated to an S3-compatible bucket you ownPostgres, filesystem, or FoundationDB
CoordinationS3 compare-and-swap ownership records — no membership protocol, failure detector, or consensusConventional clustered runtime (single Rust binary or Docker; FoundationDB option for scale)
Portability storyBring your existing Worker; run it on your machinesWrite to Rivet's API once; deploy to Rivet Cloud, self-hosted, K8s, or even Cloudflare itself
Platform breadthDeliberately narrow: cells + Workers, nothing elseFull platform surface: workflows, queues and streams, cron, realtime (WebSocket/SSE), hibernation
Ops footprintDaemon + a bucket; no control plane — but also no ingress, TLS, or domain management (bring Caddy or equivalent)Single binary or container plus your chosen database
Maturity (Sep 2026)Weeks old (launched Aug 5, 2026; v0.4.0 Aug 28), ships explicit security warningsIn production since the Dec 2024 Show HN; YC-backed, with a hosted cloud beside the OSS
Best fit"I have Workers/DO code and want it on hardware I own, now""I want an actor platform to build on, anywhere, with workflows included"

The one-paragraph verdict: celld is a compatibility play — it removes the vendor, not the API. Rivet is a platform play — it removes the vendor and gives you a different, broader API. Which one you want depends on whether you are migrating code or starting a system. Either way, both force the same four questions onto whoever operates the floor underneath: where does each actor live and how is it addressed, what happens to its state when nodes drain, who guarantees exactly-one-writer during a partition, and how do actor workloads sit beside ordinary stateless services. Those questions are the real subject of this post.


How celld works: your Worker, your machines, a bucket

Celld's design can be summarized in one sentence from its own README: every celld node embeds V8 and executes Wrangler bundles, and the fleet shares an S3-compatible bucket containing deployments, cell state, and small ownership records. Each cell — celld's name for a Durable Object — is its own SQLite database, replicated continuously to the bucket. Coordination between nodes happens through that bucket alone: object-storage compare-and-swap (conditional PUT) ensures that exactly one node owns a cell at a time, with no membership protocol, failure detector, or consensus algorithm anywhere in the loop.

Three consequences fall out of that design, and they are all deliberate. First, there is almost no control plane to operate: a daemon plus a bucket is the whole fleet. Second, scaling down is free in the strong sense — a cell no node holds is inactive and costs nearly nothing, mirroring Cloudflare's own hibernation economics without a meter attached. Third, the compatibility surface is the existing Cloudflare API, so the migration story is "bring your Wrangler bundle" rather than "rewrite your state layer." Dahl's team calls the project a "love letter" to the Durable Objects architecture, and the affection shows: this is a reimplementation, not a reimagining.

The price of that minimalism is everything celld explicitly does not do. It does not terminate public TLS or manage custom domains — self-hosted deployments need an ingress proxy such as Caddy or nginx in front of it. There is no control plane, no dashboard, no multi-tenant policy layer. The repo is weeks old and ships explicit security warnings; downstream notes from early adopters already describe it as "an unverified escape hatch, not a supported target," and the missing toolchain is visible enough that a community project (hive, a wrangler-shaped CLI on top of celld) sprang up within weeks to fill it. None of that disqualifies it — every infrastructure project starts bare — but it fixes celld's position on the maturity curve honestly: this is the escape hatch you can run today if you bring your own platform around it.

How Rivet Actors works: an actor runtime, not a clone

Rivet Actors starts from the opposite end. Where celld asks "how do we run the Cloudflare API without Cloudflare," Rivet asks "what should the stateful-serverless API have been if it were open and portable from day one." The answer is a TypeScript actor runtime with a plain thesis: an entity is an actor — a long-lived process with durable state, realtime connections, and hibernation when idle. Its December 2024 Show HN pitched it directly as "Durable Objects built with Rust, FoundationDB, Isolates," and the Apache-2.0 license was part of the pitch: built to be self-hosted and deployed on-prem.

The technical shape reflects eighteen months of production hardening that celld has not had yet. Self-hosting is a single Rust binary or Docker container backed by Postgres, the filesystem, or FoundationDB — three backends spanning "laptop demo" to "serious cluster," which matters because the state story is the whole product for actors. The runtime supports the Deno runtime for user code, so the npm and JSR ecosystems come along. Actors get storage, realtime fan-out over WebSockets and SSE, and hibernation that stops compute billing while idle — the same economic trick as celld's inactive cells, implemented one layer up. And Rivet ships the surrounding platform celld deliberately omits: durable workflows, queues and streams, cron, and a debugging story (rivet debug, hot reload).

Portability cuts the other direction from celld's. Celld is source-compatible with code written for Cloudflare; Rivet is deployment-portable for code written for Rivet — the same actor runs on Rivet Cloud, self-hosted infrastructure, locally as a library, or on Kubernetes, and Rivet even ships adapters that run other frameworks' primitives (including Vercel's workflow libraries) on Actors. That is a bigger promise and a bigger lock-in question of its own: you escape Cloudflare's API, but you marry Rivet's. For most teams that is a strictly better trade — an Apache-2.0 runtime you can self-host is not a walled garden — but it is a rewrite, not a lift-and-shift, and the honest accounting should say so.


Head-to-head: the four questions the platform has to answer

Strip away the branding and both systems are implementations of the same contract: single-writer stateful cells with storage colocated next to compute. That contract pushes four hard requirements onto whatever floor it runs on. Here is how each system answers them today.

1. Placement and addressing. Every request for actor X must reach the node currently holding X. Celld answers with the bucket: ownership records in shared object storage say which node holds which cell, claimed by compare-and-swap. Elegant, and genuinely consensus-free — but it means actor routing has a round trip to object storage on the miss path, and there is no placement policy to tune (no "keep these cells on GPU nodes" or "pin EU tenants to EU machines"). Rivet answers with a real runtime scheduler and, at the top end, FoundationDB-backed coordination: more machinery, but placement is something you can express and observe rather than something that emerges from CAS races. If your actors are fungible chat sessions, celld's answer is enough. If they have locality constraints — data residency, hardware affinity — you will feel the missing knob within a quarter.

2. Surviving drains and upgrades. Actors are long-lived; nodes are cattle. Somebody must move a cell's SQLite state off a draining node and resume it elsewhere without losing a write. Celld's story is replication-first: the cell's database is continuously replicated to the bucket, so a new owner replays from durable storage. That is the right shape — crash recovery falls out of the normal path — but on a Kubernetes fleet it leaves the orchestration to you: the drain signal, the ownership handoff, and the readiness gate that keeps traffic away until replay completes. Rivet's hibernation model was built for exactly this lifecycle (sleep, wake, migrate), and running as a container under a scheduler it already understands eviction. Either way, "state survives the node" is necessary but not sufficient; "traffic only routes to the owner that has finished replaying" is the half most self-hosted setups get wrong first.

3. Fencing: exactly-one-writer during partitions. This is the requirement the TODO spec names explicitly, and it is the one to be most skeptical about. Single-writer is easy until the network splits and two nodes both believe they own the cell. Celld's compare-and-swap gives you mutual exclusion on ownership claims — but a stale owner that has not yet noticed it lost the lease can still serve writes from its local SQLite copy. Closing that gap needs fencing: monotonic ownership epochs checked on every write path, with replication acknowledged before the write is visible. Cloudflare solves this inside its own network where partitions are rare and detection is fast; on your own fleet across regions, partitions are a when, not an if. Treat any self-hosted actor system whose fencing story is "CAS on claim" without epoch-checked writes as a demo of the happy path. Ask both projects for the partition test, and run it yourself before trusting tenant data to either.

4. Sitting beside stateless services. The prize is one platform: git-push stateless apps and stateful actors on the same fleet, same ingress, same observability. Here both projects are honest about being runtimes, not platforms. Celld needs an ingress proxy, TLS strategy, per-tenant routing, and log/metric plumbing supplied around it. Rivet supplies more of the application surface (workflows, queues, cron) but still expects a scheduler, network, and database beneath it. Neither is a PaaS; both are workloads waiting for one.

What a Kubernetes PaaS has to offer actor tenants

Those four answers compress into a checklist — the minimum a self-hosted, Kubernetes-based platform must provide before "run your actors here" is a serious offer rather than a slogan:

  • Stable addressing above churn. A routing layer that maps actor ID to current owner and converges faster than clients retry — whether backed by object-storage records, etcd, or a control-plane service, with the miss-path latency budgeted, not discovered.
  • Drain-aware handoff as a primitive. Node drains, cluster upgrades, and autoscaler scale-ins must trigger actor migration with replay-then-route semantics, not SIGKILL-and-hope. If the platform cannot orchestrate "move 500 cells off this node before the kubelet reboots it," actors do not belong on it yet.
  • Epoch-fenced writes. Ownership claims plus monotonic epochs enforced on the write path, tested under simulated partitions. This is the line between "single-writer by convention" and "single-writer by construction."
  • Shared ingress, identity, and observability. Actors behind the same domains/TLS, the same per-tenant quotas and network policy, and the same logs/metrics/traces as stateless services — otherwise you have two platforms sharing a logo.
  • A state backend with a backup story. Per-actor SQLite files in a bucket are wonderfully portable and wonderfully easy to forget to snapshot, version, and restore-test. The platform owns point-in-time recovery whether the bytes live in S3, Postgres, or FoundationDB.

Note what is not on the list: consensus. Celld's genuinely interesting contribution is proof that S3 conditional writes can replace a membership protocol for small-to-medium fleets. A platform can adopt that insight without adopting celld — the coordination mechanism is separable from the runtime.

Why the model keeps escaping, and which exit to take

Durable Objects keep getting cloned because the primitive is right and the tenancy is wrong. Colocated compute plus SQLite plus single-writer semantics is close to the ideal substrate for the workloads 2026 actually produces: agent sessions (one conversation, one writer, one event log), realtime collaboration cursors, per-user sync state, game rooms. Researchers studying agent infrastructure now describe Durable Objects as nearly ideal cloud-agent substrate whose "only real flaw has been single-vendor lock-in." Each escape hatch removes that flaw for a different audience: Rivet for teams building new stateful systems that want a platform, celld for teams holding existing Workers code that want their hardware.

So the decision is simpler than the feature matrices make it look. If you have Workers and Durable Objects in production and a fleet to put them on, celld's compatibility is the shortest path off the meter — budget the ingress, the hardening wait, and the fencing homework. If you are starting a stateful system and choosing where its next three years live, Rivet's broader surface and production history earn the rewrite. And if you operate the fleet either one lands on, the checklist above is your roadmap regardless of which runtime wins: addressing, handoff, fencing, shared ingress, backups. Get those five right and your tenants' actors survive whatever the next escape hatch looks like — including, eventually, one of these two growing up.

Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own. Stateful actor runtimes like celld and Rivet are exactly the kind of tenant workload a self-hosted PaaS should swallow whole: same fleet, same ingress, same git-push experience. Star the repo on GitHub or deploy your first app today.

Related articles

Give your agents a chain backend

Autonomous agents hit RPC endpoints very differently than people do. See what bex router handles on their behalf.

Read the agents guide