Benchmarks & limitations

What it costs, how it performs, and where it falls short

All numbers are from runs against real Cloudflare deployments in October 2026 (raw logs in bench/results). Where the pool is worse than real Durable Objects, this page says so.

≈180×
cheaper for 1,000 always-awake objects ($22 vs $4,050 / month)
55 / 55
API parity tests pass on all 5 backends
0
acknowledged writes lost in planned moves, sleeps and deploys (≈11.5k writes tested)
≤ 30 s
of writes at risk if a node dies abruptly; a real DO loses none

Cost

Real Durable Objects bill wall-clock time at 128 MB per awake object. The pool bills a container's memory plus the CPU it actually uses. The pool wins once objects are awake more than about 0.3% of the time (~2 h/month). Below that, hibernating real DOs are cheaper.

Workload (per month)Real DOdo-pool
100 always-awake objects$405$13
1,000 always-awake objects$4,050$22
10,000 always-awake objects$40,500$188
1,000 objects awake 5% of the time$203$13
1,000 objects awake 0.1% of the time$4$13
Chatty rooms: 50 × 10 users × 5 msg/s$251$124

List prices; free allowances ignored. Pool capacity is measured: 1,000 awake 1 Hz objects on ¼ vCPU billed ≈ 0.2 vCPU. v2 nodes default to 1 vCPU / 2 GiB because the new scheduling mode doesn't accept the ¼-vCPU size, which is similar per object.

Where the money goes

  • Real DO: 0.125 GB × every awake second × $12.50 per million GB-s = $4.05 per always-awake object per month, plus requests (incoming WebSocket messages at 1/20). Outgoing frames are free.
  • do-pool: container memory ($0.0000025 per GiB-s) + disk + active CPU ($0.00002 per vCPU-s), plus 2 R2 PUTs per node per 30 s. The fronting PoolNode DO isn't billed for wall-clock time on raw-passthrough sockets (measured).
  • Fan-out flips it. Outgoing frames are free on real DOs but cost CPU in a container, so chatty broadcast rooms come out only ~2× cheaper.

Recovery & availability

How long until an object answers again after each kind of event. Time is on a log scale. A real Durable Object has no equivalent for most of these, because Cloudflare handles them invisibly.

Performance & density

MeasureReal DOdo-pool
Memory per resident objectbilled as 128 MB~0.2 MB (SQLite object in workerd)
Memory per WebSocketn/a (not billed)~77 KB; ~370 KB with permessage-deflate (we strip it)
Throughputeach object has its own thread~11k WebSocket frames/s per vCPU (naive broadcast); hosts vary ~1.7×
Awake 1 Hz objects per ¼ vCPUn/a~1,000 comfortably; saturates at ~1,500–1,700
Connections per containern/ahard cap 4,096 (platform limit)
Browser round trip (cursors demo)18–114 msv1: 82 ms (container far away) · v2: 97 ms (region-local)
KV get from an object (median)5 ms19–30 ms (via the bridge)
Service-binding RPC from an object~0 ms (same thread)16–23 ms
DO → DO RPC36–37 ms31–33 ms (via Placement)

Round-trip numbers come from a home connection in California and are noisy. In v1 containers started wherever Cloudflare had room (we saw Paris and Osaka). In v2 they start in the same city as their node DO.

OperationMeasured
Checkpoint writes2 R2 PUTs per node per 30 s, regardless of object count
Drain 690 objects (one segment)2.8 s
Filesystem snapshot (sleep)2.5–4.4 s · 0.2–10 MB
Restore from snapshot0.6–2.2 s
Load app code (facet), cold / warm~3 ms / ~0 ms
Memory per app version / per object~2.2 MB / ~0.4 MB on shared code
Sandboxed tenant process~17 MB, ~25 ms to start

Limitations: read these before using it

Durability

  • Writes are acknowledged before they're durable. A node that dies without draining loses up to 30 s of writes. Real DOs replicate first. Don't put money or anything irreplaceable in a pooled object.
  • No point-in-time recovery. Old R2 segments are never compacted or garbage-collected yet.

Deploys

  • Under Cloudflare's durable_object scheduling, a container dies with its DO, so deploying the control-plane Worker restarts every node over ~1–13 min. No acknowledged writes were lost in tests, but one write stalled for 32 s.
  • Edge Worker deploys don't touch the pool, which is why it's split into two Workers.

Scale & shape

  • A single Placement DO handles every new placement; lookups are cached per isolate.
  • 4,096 connections per container; one hot object competes with its neighbours for CPU.
  • workerd doesn't enforce per-tenant CPU limits; the sandboxed tier gives each app its own process instead.

API gaps

  • Bindings (KV, R2, AI, services, other DOs) work with the same code, but each call adds ~15–25 ms (it's replayed in the control Worker), bodies are buffered rather than streamed, and WebSockets can't go over a binding. Sandboxed apps get no bindings.
  • Moves close WebSockets with 1012; clients must reconnect (most libraries do).
  • Facets can't own alarms anywhere (Cloudflare or workerd); the host proxies them.

How these were measured

test/parity.test.ts runs the same API assertions against all five backends. test/lifecycle.test.ts exercises moves, crashes, deploys, sleep/wake and the sandbox, each against the live deployment. bench/ has the load generator, analytics pull (billing data from Cloudflare's GraphQL API) and cost model. Total Cloudflare spend for everything: about $1.20.