IAnswerable

Cloudflare KV, D1, or Durable Objects

19 Oct 2026

KV, D1, and Durable Objects solve different problems. Picking the wrong one means either eventual-consistency bugs you didn’t expect, or a bill for consistency you didn’t need.

The one-line version of each

Decision flow

flowchart TD
    A["Need to store data?"] --> B{"Written often,\nread by many?"}
    B -->|"Write-heavy"| C{"Need strict\nconsistency?"}
    B -->|"Read-heavy,\nrarely changes"| D["KV"]
    C -->|"Yes - counters,\nlocks, sessions"| E["Durable Objects"]
    C -->|"No - just need\nqueries/relations"| F["D1"]
  

Where each one breaks if you pick wrong

KV for session data. KV writes propagate globally but aren’t instant — a write in one region can take time to be visible everywhere (Cloudflare’s own docs describe this as eventual consistency, typically settling within seconds, not guaranteed). If you write a session token in one request and read it in the next request routed to a different colo, you can get a stale read. You’ll see intermittent “session not found” errors that don’t reproduce locally, because local dev doesn’t have geographic propagation delay.

D1 for a global counter. D1 is per-database, not a distributed counter. Concurrent writes from different regions to the same row will serialize through SQLite’s locking, which means high write concurrency on a single row turns into a queue. You’ll see increased latency under load, not corruption — but it’s not built for a “like button with a million concurrent clicks” pattern.

Durable Objects for bulk analytics reads. A Durable Object is single-threaded and pinned to one location. If you route heavy read traffic from around the world to a single DO, every one of those reads waits behind the same execution queue and pays the round-trip to that DO’s location. You’ll see rising latency as traffic grows, and it won’t shard on its own — you’d need to manually partition data across multiple DOs.

A concrete mapping

Use case Store
Feature flags, config KV
Rendered page cache KV
User accounts, orders, relational data D1
Anything with WHERE, JOIN, or COUNT D1
Real-time chat room state Durable Objects
Rate limiter / distributed lock Durable Objects
WebSocket connection coordination Durable Objects

The practical rule

Start with D1 for anything that looks like normal app data — it’s the one that behaves like what you already know from Postgres or SQLite. Reach for KV only when you’ve confirmed the access pattern is read-heavy and can tolerate a few seconds of staleness. Reach for Durable Objects only when correctness under concurrent access is the actual requirement, not just “this data changes a lot” — that’s still D1’s job.