Terse vs Cloudflare Durable Objects: choosing an actor runtime

Choose Cloudflare Durable Objects when you want Cloudflare to operate your stateful application infrastructure. Choose Terse Durable Actors when you want to operate an open-source actor runtime, with explicit persistence and concurrency behavior in your application code. Both are relevant to shared chat sessions, collaborative applications, and coordinating agents.

We build Terse. This comparison explains where our approach fits and where Cloudflare is the better choice. It compares the documentation available on October 17, 2026; it is not a performance benchmark.

What are we comparing?

An actor gives a piece of your application a stable identity and ownership of its state. You might create one actor per document, chat, or customer account. Callers address that identity instead of deciding which server currently owns the data.

Cloudflare Durable Objects combine compute and storage on Cloudflare's platform. Terse Durable Actors provide an MIT-licensed runtime with TypeScript and Python SDKs, generated clients, and a documented self-hosting path.

The first decision is operational: who should run the system that starts actors, routes requests, and protects their data?

Terse vs Cloudflare at a glance

Decision Terse Durable Actors Cloudflare Durable Objects
Production deployment Self-host; documented GCP path Managed Cloudflare platform
Application model Actor classes, decorated fields, generated clients Durable Object classes and Workers bindings
Persistence interface Persisted fields and per-actor SQLite Storage APIs, including SQLite-backed storage
Concurrency Calls serialized across awaits by default; explicit interleaving Single-threaded execution with storage gates; async interleaving needs care
Realtime Socket hooks and broadcast helpers WebSockets and a hibernation API
Operating responsibility Your team operates the deployment Cloudflare operates the service

The sections below link to the documentation behind these differences.

Deployment control comes with operating work

Terse's self-hosting guide currently supports Google Cloud: GKE Sandbox, PostgreSQL, a GCS bucket, and HTTPS ingress with WebSocket support. A Helm chart installs the runtime. Your team manages credentials, capacity, upgrades, backups, and recovery.

That is useful when your infrastructure team needs to inspect and operate the actor system. It also means a Helm chart is only part of the deployment work. Open source does not automatically mean a supported deployment on every cloud.

Cloudflare is attractive when the team wants a managed platform and already builds with Workers. Evaluate that benefit against the cost of adapting application code and operations to its platform interfaces. Durable Objects have their own storage; using them does not inherently require you to adopt D1 or R2. See Cloudflare's storage reference.

Persistence starts with what survives a restart

In Terse, a persisted field makes the durable part of a class explicit. This small actor records a session title:

import { Actor, Persisted } from "durable-actors"

export class Session extends Actor {
  @Persisted private title = "Untitled"

  async rename(title: string): Promise<string> {
    this.title = title
    return this.title
  }

  async getTitle(): Promise<string> {
    return this.title
  }
}

The TypeScript guide documents commits of persisted fields and SQLite changes together after successful calls. Under the default execution model, failed calls roll back those changes. External API calls are outside that transaction.

Cloudflare exposes storage explicitly through the object context. Its SQLite-backed storage is private to each object and includes transactional APIs and point-in-time recovery. Ordinary class fields are in-memory state, so durable data needs a storage operation. See the storage API and in-memory state reference.

For an evaluation, rename a session, restart its runtime, and read the title. Then introduce a failure after a mutation and check the documented result. That exercise tells you more than a table cell saying both systems support persistence.

Concurrency matters when an agent waits

Imagine an agent waiting for a model response while a user asks for session status. Should the status call wait too?

Terse serializes calls across awaits by default. Its @Interleave decorator lets other calls proceed while a method awaits. There is an important tradeoff: once any method in a class uses interleaving, calls throughout that class lose automatic rollback. Design that boundary deliberately. The interleaving documentation explains the behavior.

Cloudflare objects are single-threaded, but that does not imply every async handler runs uninterrupted. Storage gates protect particular operations; external awaits still require careful reasoning about shared state. Cloudflare's rules of Durable Objects cover these patterns.

For either system, test two callers updating the same session while one waits on an external service. Keep payment, email, and model-provider retries idempotent: local persistence cannot make an external side effect happen exactly once.

WebSockets, resource limits, and cost

Terse provides connection hooks, typed socket messages, and broadcasts. Cloudflare provides WebSockets and WebSocket hibernation, which can retain connections while an idle object leaves memory. Neither system needs to be dismissed as unsuitable for realtime applications.

Resource needs should be measured with the intended workload. Terse exposes per-class CPU and memory configuration; your deployment still needs capacity. Cloudflare publishes platform limits. A request's active CPU time is different from time spent waiting for a model response.

For cost, compare a realistic month of requests, storage operations, connected clients, and idle time. Use Cloudflare's current pricing against your Terse infrastructure bill and operating time. We have not established a cost or speed winner with a matched benchmark.

Migrating from Durable Objects to Terse

Treat Terse as an architectural alternative rather than an API-compatible replacement. A migration needs changes to object lookup, persistence calls, socket handling, and deployment.

  1. Pick a bounded workload, such as one room or session, and preserve its identity scheme.
  2. Map stored records to persisted fields or SQLite tables. Define a state export and import process.
  3. Replace Workers bindings with generated actor clients and implement authorization at the application boundary.
  4. Exercise restart, concurrent calls, failed calls, and socket reconnects before moving production traffic.
  5. Plan cutover and rollback so only one system owns writes for a given identity at a time.

Keep the initial experiment small enough that you can inspect every state transition.

Which should you choose?

Choose Cloudflare Durable Objects when managed operations, Workers integration, and Cloudflare's documented storage and connection lifecycle meet your needs.

Choose Terse Durable Actors when owning the runtime is a requirement, its class-based API fits your application, and your team can operate the documented infrastructure.

If you are also evaluating Rivet, read Terse vs Rivet Actors. That comparison looks at another self-hostable option and a different state-saving model.

Try a session locally

Follow the Durable Actors quickstart to scaffold a project and run the local server:

bunx durable-actors init my-actors
cd my-actors
bun install
bunx durable-actors dev

Use the TypeScript guide to add the session actor and generate its client. Start with a restart test, then add a second caller and a WebSocket connection.

Build your first Durable Actor →