Cloudflare Durable Objects SQLite vs D1 Selection Playbook

2026-04-06 · software

Cloudflare Durable Objects SQLite vs D1 Selection Playbook

Date: 2026-04-06
Category: knowledge
Domain: software / cloudflare / distributed systems

Why this matters

Cloudflare now gives you two very different ways to run SQLite-shaped workloads at the edge:

At first glance they can look confusingly similar because both speak SQLite-ish SQL and both live inside the Workers ecosystem. But architecturally they solve different problems.

If you pick the wrong one, you usually get one of these failure modes:

The clean mental split is:

That sounds abstract, but the design consequences are huge.


1) Fast mental model

D1

Think of D1 as:

Good default fit:

SQLite-backed Durable Objects

Think of SQLite-backed Durable Objects as:

Good default fit:

The key difference:


2) The decisive architectural difference

The most important question is not “Do I need SQL?” It is:

Do I need a managed relational database, or do I need serialized stateful compute around each entity?

Pick D1 when the center of gravity is the database

Choose D1 when your application naturally wants:

Typical examples:

Pick Durable Objects when the center of gravity is coordination

Choose SQLite-backed Durable Objects when your real problem is:

Typical examples:

This is why Cloudflare’s own docs frame Durable Objects as a lower-level compute-with-storage building block for distributed systems, while D1 is the managed SQL database.


3) What D1 is especially good at

A) Managed SQL without inventing your own database layer

D1 is the simpler choice when you want standard relational storage and do not want to design your own sharding and routing scheme around identities.

That simplicity matters. A lot of systems do not actually need actor-style coordination; they just need a database.

B) Read-heavy global apps

D1’s read replication is a real advantage when:

Cloudflare’s docs make an important point here: read replication only helps if you use the D1 Sessions API. Without Sessions API, reads keep going to the primary.

So the operational rule is:

C) Cross-entity querying is natural

D1 is better when you want SQL to span lots of entities naturally:

D) Managed recovery and familiar database ergonomics

D1 gives you:

That is often exactly what you want.


4) What SQLite-backed Durable Objects are especially good at

A) Zero-network database access inside the object

The big architectural trick of SQLite-backed Durable Objects is that SQLite is embedded in the same execution context as the object logic.

That means:

This is not just a performance detail. It changes how much coordination logic feels practical to write.

B) Strict serialization per entity

If all operations for entity X must happen in order, Durable Objects are a better conceptual fit than “every request hits a shared DB and we hope constraints/transactions are enough.”

Examples:

C) Stateful compute + storage + alarms in one place

This combination is easy to underrate. A SQLite-backed Durable Object can combine:

That makes it excellent for entity-local control loops.

D) Per-entity sharding as a first-class design

Durable Objects scale out by having many objects, not one giant one. The docs explicitly warn against creating a single global object.

This is the right pattern:

This is the wrong pattern:

If your design can be cleanly decomposed into many independent identities, Durable Objects are powerful. If not, they can become an awkward box.


5) The easiest decision rule

Use this quick chooser:

Start with D1 if:

Start with SQLite-backed Durable Objects if:

Use both when:

In practice, “use both” is often the winning architecture.


6) The deepest mistake to avoid: confusing per-entity SQL with shared relational SQL

Because Durable Objects now have SQLite, it is tempting to think:

“Great, I can just use Durable Objects as my database.”

Sometimes yes. Often no.

The question is whether your SQL is:

Entity-local SQL

Good fit for Durable Objects.

Examples:

Global / cross-entity SQL

Usually better fit for D1.

Examples:

Durable Objects make local state elegant. They do not magically make global relational querying free.

If you routinely need cross-object joins, you probably want D1 for that layer.


7) Performance reality, without fairy dust

D1 performance shape

Cloudflare documents that each individual D1 database is inherently single-threaded and processes queries one at a time. Throughput depends heavily on query duration. They even give rough guidance:

That is a useful reminder: D1 is not a magical infinitely parallel database. Each DB has a serial bottleneck, though read replication changes the read path shape.

Durable Object performance shape

Cloudflare’s Durable Object guidance says a single object can handle roughly 500–1,000 requests/sec for simple operations, depending on work per request.

That means the design question is not “Can one object scale forever?” It is:

The real trade-off

So performance success is usually about choosing the right unit of scale:


8) Consistency model differences that matter in app design

Durable Objects

Durable Objects are the cleaner choice when you need strict serialized handling per entity. That is their superpower.

You can reason about one object as one authoritative owner of one stream of state transitions. That drastically simplifies a whole class of race conditions.

D1

D1 is relational and single-primary for writes, but when using read replication there is a very important nuance:

This is good, but it is a different mental model from “one authoritative object serializes everything for entity X.”

Practical translation:


9) Operational details people miss

A) New Durable Object classes should use SQLite storage

Cloudflare now explicitly recommends SQLite-backed Durable Objects for new namespaces. That is the modern default.

B) Durable Object migrations are about class/runtime mapping, not SQL schema migrations

This trips people up. Durable Object migrations in Wrangler are required when you:

That is different from your own internal SQL schema evolution inside the object.

C) Deleting Durable Object storage is not just “drop a table”

Cloudflare’s docs are explicit: if you want a Durable Object to fully cease to exist and stop being billed for storage, you must use:

Just deleting rows or dropping tables is not enough because metadata can remain.

That is a subtle but very real cost/cleanup footgun.

D) D1 read replication is not automatic magic

If you do not adopt the Sessions API flow, you do not really get the intended read replication behavior.

This is the kind of thing that leads to false expectations like:

“we enabled replication but latency barely changed.”

E) D1 database size limits shape architecture

Cloudflare documents D1 as a scale-out-across-many-smaller-databases product, with per-database limits such as:

So if your mental model is “one endlessly growing monolithic database,” D1 is the wrong emotional model. It wants deliberate partitioning when data grows.


10) Decision matrix

Situation Better default
Standard app data with normal SQL tables D1
One authoritative coordinator per entity Durable Objects
Cross-entity admin queries D1
WebSocket room/session state Durable Objects
Read-heavy global users close to replicas D1
Per-document / per-room / per-user hot mutable state Durable Objects
Need alarms/timers tightly coupled to entity state Durable Objects
Need a managed DB more than a distributed primitive D1
Natural shard key is obvious and important Durable Objects
Broad filtering / ranking / listing across all data D1

11) Patterns that work well

Pattern A — Durable Objects for hot coordination, D1 for product views

Use Durable Objects for:

Use D1 for:

This is often the best “real app” split.

Pattern B — One D1 DB per tenant, plus Durable Objects for the hottest paths

If tenants are natural isolation boundaries:

This avoids forcing every request through actor-style routing while still giving you coordination where it matters.

Pattern C — Durable Objects only, but only when the world is truly entity-centric

This can work for systems like:

But be honest: if you later need broad reporting/search across all objects, you will likely add D1 or another index/query layer anyway.


12) Anti-patterns

Anti-pattern 1: single global Durable Object

This is the classic self-own. You recreated a giant bottleneck and threw away the scale-out model.

Anti-pattern 2: using Durable Objects when you mostly want ad-hoc SQL

If your main need is “let me query across everything cleanly,” D1 is the saner tool.

Anti-pattern 3: using D1 to model highly contentious per-entity command serialization

You can force it, but you are making the coordination problem harder than it needs to be.

Anti-pattern 4: enabling D1 read replication but not propagating session/bookmark context

Then you do not actually get the consistency/latency model you think you bought.

Anti-pattern 5: treating Durable Object lifecycle cleanup casually

If you create lots of ephemeral objects, understand storage cleanup rules or you will accumulate billable leftovers.


13) If I were choosing for common workloads

Realtime multiplayer / collaboration

Prefer Durable Objects first. The coordination model matches the problem. Use D1 later for analytics, search, account metadata, or cross-room views.

SaaS app with dashboards, users, teams, content

Prefer D1 first. Use Durable Objects only for the small set of contention-heavy realtime flows.

Per-user notebook / agent / workspace state

Usually:

Event-sourced or command-driven workflows

If commands must be serialized per entity, Durable Objects are very attractive. D1 can still be the broader read/query layer.


14) A brutally practical selection heuristic

Ask these in order:

  1. What is my atom of coordination?

    • If obvious and important → Durable Objects gets stronger.
  2. Do I need frequent cross-entity SQL?

    • If yes → D1 gets stronger.
  3. Is the hot path contention-heavy on a single entity/key?

    • If yes → Durable Objects gets much stronger.
  4. Is the workload globally read-heavy with ordinary relational access patterns?

    • If yes → D1 gets much stronger.
  5. Do I need timers/alarms/live connections coupled to state?

    • If yes → Durable Objects.
  6. Am I secretly designing a global singleton?

    • If yes → stop and redesign.

If you cannot clearly answer what your coordination atom is, that is usually a sign you should start with D1, not Durable Objects.


15) Evidence anchors / further reading

Official docs and Cloudflare materials:


Final take

If you reduce the choice to “which Cloudflare SQL thing should I use?”, you will make bad architecture decisions.

The real choice is:

Use D1 when your app wants a database. Use Durable Objects when your app wants an owner for each piece of state. Use both when your hot path and your query path are different systems pretending to be one product.

That last case is common. And usually correct.