Boomspot
  • Home
Loading...
Boomspot

Daily tech news, software development coverage, Apple reporting, and the gear behind modern music making.

TwitterLinkedIn

Browse

  • Categories
  • Tags
  • Authors

Company

  • About
  • Contact

Legal

  • Privacy Policy
  • Terms of Service
  • Unsubscribe

© 2026 Boomspot. All rights reserved.

Built by Boomspot
Updated hourly

AI Content Disclosure: Articles on Boomspot are researched, written, and edited with the assistance of advanced AI systems. We combine software-assisted research with editorial oversight to deliver useful, accurate, and practical technical and music production content. Learn more about our editorial approach.

  1. Home
  2. Coding
  3. Cloudflare Workers D1 vs KV: Which Storage to Use?
coding7 min read

Cloudflare Workers D1 vs KV: Which Storage to Use?

D1, KV, R2, or Durable Objects? A practical guide mapping real API data patterns to the right Cloudflare Workers storage binding.

S

Staff

September 5, 2026

Cloudflare Workers D1 vs KV: Which Storage to Use?

Pick the wrong storage binding on Cloudflare Workers and you will not find out until your app is live and either slow, inconsistent, or expensive. D1 and KV solve different problems, and R2 and Durable Objects sit alongside them for cases neither one handles well. This guide maps the data patterns you actually build against, so you choose correctly the first time.

The short version: D1 is for relational data with real query needs, KV is for fast reads of simple values that tolerate staleness, R2 is for files, and Durable Objects are for state that must stay consistent across concurrent requests. The rest of this article explains why, with the tradeoffs that matter in production.

User records and relational data belong in D1

D1 is Cloudflare's serverless SQLite database, and it is the right home for anything you would normally put in Postgres or MySQL: user accounts, orders, permissions, anything with relationships between rows. If you need to query "all users who signed up last week and have an active subscription," you need SQL, and D1 gives you that directly.

const { results } = await env.DB.prepare(
  "SELECT id, email, plan FROM users WHERE created_at > ? AND active = 1"
).bind(weekAgo).all();

D1 reads are fast when served from a location near the database's primary, but writes are strongly consistent and go through a single primary per database. That matters: D1 is not a globally distributed multi-writer system. If your API writes user records from users scattered across continents, expect meaningfully higher write latency for requests far from the primary region compared to a globally replicated KV read. For most CRUD-style APIs this is a non-issue; for high-frequency global writes it is worth benchmarking before you commit.

D1 pricing follows a request- and storage-based model rather than KV's flat per-operation model, so cost scales with query volume and row count, not raw key count. If your data has foreign keys, needs transactions, or requires filtering and aggregation, D1 is the only sane choice among Workers bindings.

Session and config data is exactly what KV was built for

KV is a globally replicated key-value store optimized for reads that vastly outnumber writes. That profile fits session tokens, feature flags, API configuration, and cached lookups almost perfectly. A session check on every request is a textbook KV use case.

const session = await env.SESSIONS.get(sessionId, { type: "json" });
if (!session) return new Response("Unauthorized", { status: 401 });

The catch is consistency. KV is eventually consistent, and writes can take a noticeable window, often up to roughly a minute in edge cache propagation terms, before every location sees the update. If your app writes a session and needs that write visible everywhere immediately, KV will occasionally serve stale data to a different edge location. For session data this is usually harmless since the same user typically keeps hitting the same nearby edge node. For config flags meant to flip instantly for every user everywhere, it's a real risk you should plan around.

KV also enforces a value size ceiling (in the low tens of megabytes) and works best with values well under that, since it is not designed as a document store for large blobs. Use it for small, frequently read values, not large JSON payloads that change often. For more on this, see more on claude on aws: bedrock vs platform security checklist.

File uploads and binary data should never touch D1 or KV

Neither D1 nor KV is designed to hold images, PDFs, or video. D1 rows have practical size limits that make blob storage awkward and expensive, and KV's value size cap plus per-operation pricing makes it a poor fit for large binaries you read repeatedly.

R2 is Cloudflare's S3-compatible object storage, and it is the correct binding for uploaded files. It charges no egress fees, which matters if your API serves files to users outside Cloudflare's network, a scenario where S3-style egress costs quietly balloon.

await env.UPLOADS.put(`avatars/${userId}.png`, file.stream());
const object = await env.UPLOADS.get(`avatars/${userId}.png`);
return new Response(object.body, {
  headers: { "content-type": object.httpMetadata.contentType }
});

A common pattern: store the file in R2, then store a reference to its key in a D1 row alongside the rest of the record's metadata. D1 handles "what exists and who owns it," R2 handles "where the bytes live." Do not try to make KV hold file references as your source of truth if you need strong guarantees about which file is current, since a stale KV read could point a user to metadata for a file that has already been replaced. See best llm quant format for every apple silicon chip: the details for additional background.

Real-time and coordinated state needs Durable Objects, not KV

This is where teams most often get burned. KV's eventual consistency means it cannot safely coordinate state that multiple requests need to agree on right now, things like a live auction bid, a chat room's active participant list, a rate limiter counting requests per second, or a websocket connection registry. Two requests hitting different edge locations can read stale KV values and both "win" a race that should have had one winner.

Durable Objects solve this by giving you a single, strongly consistent instance of an object per unique ID, with in-memory state and access to persistent storage tied to that specific object. Every request for a given ID gets routed to the same instance, so there is no split-brain problem.

Also read: related topic: bar chart vs histogram: when to use each in python

export class RateLimiter {
  constructor(state, env) {
    this.state = state;
  }
  async fetch(request) {
    const count = (await this.state.storage.get("count")) || 0;
    if (count > 100) return new Response("Too many requests", { status: 429 });
    await this.state.storage.put("count", count + 1);
    return new Response("ok");
  }
}

The tradeoff is that a Durable Object is pinned to one location for its lifetime, so requests from the other side of the world incur real round-trip latency to reach it. That is the price of strong consistency. Use Durable Objects sparingly, for the specific slice of state that genuinely needs coordination, and keep everything else in D1 or KV where global distribution works in your favor rather than against you.

Putting the tradeoffs side by side

Here is how the four bindings compare across the dimensions that actually drive decisions:

  • D1: Strong consistency per database, single-primary writes, best for relational queries, pricing tied to rows read/written and storage, not ideal for globally hot writes.
  • KV: Eventually consistent (propagation can take up to roughly a minute), extremely low-latency reads from any edge location, flat per-operation pricing, best for read-heavy simple values under its size limit.
  • R2: Strongly consistent object storage, no egress fees, best for files and binaries, pricing based on storage and operations rather than bandwidth out.
  • Durable Objects: Strong consistency via single-instance coordination, higher latency for far-away requests, pricing based on requests and duration, best for state that must never race.

A useful mental shortcut: ask whether two simultaneous requests could disagree about the correct answer. If yes, and that disagreement matters, you need D1 or Durable Objects. If stale-by-a-few-seconds is acceptable, KV is faster and cheaper. If you are storing bytes rather than facts, it's R2 every time.

Most real Workers APIs end up using three or four of these bindings together rather than picking just one. A typical setup stores user and order records in D1, caches session tokens and feature flags in KV, keeps uploaded files in R2, and reserves Durable Objects for the one or two features, like a live leaderboard or a websocket hub, that genuinely need tight coordination. Treat the choice as a mapping exercise for each piece of data you handle, not a single platform-wide decision, and you'll avoid both the performance cliffs and the consistency bugs that come from forcing every dataset into the same binding.

Tags

Developer ToolsCloud ComputingCoding Best PracticesSoftware DevelopmentWeb Development

Related Articles

How to Use Claude Code Subagents to Parallelize Development
coding•3 min read

How to Use Claude Code Subagents to Parallelize Development

Learn how to enhance your development workflow using Claude Code Subagents. This guide provides practical examples for parallelizing coding tasks.

Sep 13, 2025

Unlocking ChatGPT Developer Mode: Full MCP Client Access
coding•4 min read

Unlocking ChatGPT Developer Mode: Full MCP Client Access

Unlock the power of ChatGPT Developer Mode with full MCP client access. Discover how to enhance your coding projects and streamline development.

Sep 11, 2025

Mastering MCP Elicitation for Enhanced AI Interactions
coding•3 min read

Mastering MCP Elicitation for Enhanced AI Interactions

Discover the power of MCP elicitation in creating seamless AI interactions, from streamlining development to improving user satisfaction.

Sep 10, 2025

Browse by Category

Technology609Coding151Linux25SEO20Music Production15Apple Rumors11Studio Gear7

Popular Posts

AIR Fabric Vol 2: Andromeda vs Matrix 12 vs CS-80 Review

AIR Fabric Vol 2: Andromeda vs Matrix 12 vs CS-80 Review

6 min read
AI Coding Agent Cost Ledger: Track Expensive Sessions

AI Coding Agent Cost Ledger: Track Expensive Sessions

7 min read
Read This Before You Buy That TV Streaming Stick

Read This Before You Buy That TV Streaming Stick

6 min read
Landing Pages vs Full Web Apps: Dastarkhwan Case Study

Landing Pages vs Full Web Apps: Dastarkhwan Case Study

5 min read
Harley Benton Space Wah & Volume: 3 New Pedals Compared

Harley Benton Space Wah & Volume: 3 New Pedals Compared

6 min read

Recent Posts

Sample Size Calculator for Robot Policy Comparison

Sample Size Calculator for Robot Policy Comparison

Sep 5, 2026•6 min
Best LLM Quant Format for Every Apple Silicon Chip

Best LLM Quant Format for Every Apple Silicon Chip

Sep 5, 2026•5 min
How to Recreate That Lo-Fi PS1 Synth Sound in Your DAW

How to Recreate That Lo-Fi PS1 Synth Sound in Your DAW

Sep 4, 2026•6 min
How to Justify an SEO Budget to Skeptical Leadership

How to Justify an SEO Budget to Skeptical Leadership

Sep 4, 2026•6 min
How to Justify SEO Spend to a Skeptical CFO

How to Justify SEO Spend to a Skeptical CFO

Sep 4, 2026•6 min