Serverless compute solved the “no servers to manage” problem for application code, but databases were the awkward holdout. You could run a Worker on the edge in milliseconds, then watch it wait on a database sitting in a single far-away region. Cloudflare D1 closes that gap: it is a real SQL database, built on SQLite, that lives on Cloudflare’s network and binds directly to your Workers. You write ordinary SQL, and you are billed only for the queries you actually run.
This guide explains what D1 is, what it costs, how to query it from a Worker, and when to reach for it over KV or a traditional Postgres database.
TL;DR
- D1 is a serverless SQL database built on SQLite, bound directly to Cloudflare Workers
- The free tier is generous: 5 million rows read and 100,000 rows written per day, plus 5 GB storage
- You are billed for rows read and written, not for uptime: idle databases cost nothing
- It uses familiar SQL with prepared statements, so there is almost no learning curve
- It pairs with the rest of the platform, Workers AI , R2 and KV, to build complete apps
- Manage your databases visually with my free Easy Cloudflare D1 desktop app
What Cloudflare D1 Is
Cloudflare D1 is a managed, serverless database: a relational SQL store built on SQLite, one of the most battle-tested database engines in the world. “Serverless” here means you never provision an instance, choose a size, or keep anything running. You create a database, bind it to a Worker, and run SQL. Cloudflare handles storage, durability and scaling.
Because it speaks standard SQL, D1 feels immediately familiar to anyone who has used a relational database. You define tables, write SELECT, INSERT, UPDATE and DELETE statements, create indexes, and use transactions. The difference is operational: there is no connection pool to tune, no server to patch, and no idle cost.
Cloudflare D1 Pricing and Free Tier
D1’s billing model is unusual in the best way: you pay for queries, measured in rows read and written, not for time. An idle database costs nothing because there is no instance running. From the official D1 pricing for 2026:
Workers Free plan
| Resource | Daily limit |
|---|---|
| Rows read | 5 million per day |
| Rows written | 100,000 per day |
| Storage | 5 GB total |
Workers Paid plan
| Resource | Included, then |
|---|---|
| Rows read | First 25 billion / month, then $0.001 per million |
| Rows written | First 50 million / month, then $1.00 per million |
| Storage | First 5 GB, then $0.75 per GB-month |
There are no data transfer or egress fees. A useful optimisation detail: indexes can dramatically reduce billable row reads by letting queries scan fewer rows, at the cost of a small increase in write volume. For read-heavy workloads, good indexing keeps both your latency and your bill low.
Querying D1 From a Worker
You bind a D1 database in your wrangler.toml, then access it through env.DB. D1 uses prepared statements, which keep your queries safe from SQL injection by separating the query text from the values.
1export default {
2 async fetch(request, env) {
3 // Read with a parameterised query
4 const { results } = await env.DB
5 .prepare("SELECT id, title FROM posts WHERE published = ? ORDER BY created_at DESC LIMIT 10")
6 .bind(1)
7 .all();
8
9 return Response.json(results);
10 },
11};
Inserting data follows the same pattern:
1await env.DB
2 .prepare("INSERT INTO posts (title, body, published) VALUES (?, ?, ?)")
3 .bind("Hello D1", "My first edge database post", 1)
4 .run();
You can also batch multiple statements to run them efficiently in one round-trip, which is the recommended approach when you have several related writes.
Cloudflare D1 vs KV vs Postgres
Choosing the right storage primitive matters. Here is how D1 compares to the two it most often gets weighed against.
| Need | Best choice | Why |
|---|---|---|
| Relational data, queries, joins | D1 | Real SQL with tables, indexes and transactions |
| Simple key-value lookups, config, caching | KV | Optimised for fast reads of single keys at the edge |
| Large existing relational workload, complex extensions | Postgres (e.g. via Hyperdrive) | Mature engine for heavy, feature-rich workloads |
In short: use KV when you just need to fetch a value by a key as fast as possible. Use D1 when your data is relational and you want to query it with SQL. Reach for a full Postgres database (which Workers can connect to, accelerated by Hyperdrive) when you have a large, established relational workload or need Postgres-specific features that SQLite does not offer.
When Cloudflare D1 Is the Right Choice
D1 is an excellent fit when:
- Your data is relational and benefits from SQL queries, joins and indexes
- You are building on Workers and want your database in the same platform
- Your workload is spiky or low-to-moderate, so pay-per-query beats a always-on instance
- You want zero operational overhead: no provisioning, patching or connection pooling
- You are happy within SQLite’s feature set, which covers the vast majority of application needs
It is less suited to very large datasets that exceed its limits, or workloads that depend on Postgres-specific extensions. For those, connect a dedicated database from your Worker instead.
A Realistic Use Case
A typical D1 application: a blog or small SaaS backend where a Worker serves requests, queries posts or user records from D1, stores uploaded files and images in R2 , caches hot values in KV, and optionally calls Workers AI for a feature like summarisation. Everything runs on one platform, on the edge, with no servers and a bill that scales with actual usage. This is the same architecture pattern behind the Cloudflare Pages user system I built.
Managing D1 Visually
Running migrations and inspecting data through the command line is fine, but a graphical client makes everyday work faster. My free Easy Cloudflare D1 desktop app lets you browse databases and tables, run queries, and manage your D1 data from a clean interface on Windows, macOS and Linux, with your credentials encrypted locally on your own machine. And if your stack pairs D1 with KV for caching, my free Easy Cloudflare KV app does the same for your key-value namespaces.
Key Takeaways
- D1 is a serverless SQL database built on SQLite, bound directly to Cloudflare Workers
- The free tier covers 5 million reads and 100,000 writes per day with 5 GB storage
- Billing is per row read and written, not per hour; idle databases cost nothing
- It uses standard SQL with safe prepared statements, so the learning curve is minimal
- Use KV for key-value lookups, D1 for relational SQL, and dedicated Postgres for heavy or extension-specific workloads
- D1 combines with R2, KV and Workers AI to build complete edge applications
Frequently Asked Questions
What is Cloudflare D1? D1 is a serverless SQL database built on SQLite that runs on Cloudflare’s network and binds directly to Workers. You write standard SQL and never provision or manage a server. Cloudflare handles storage, durability and scaling.
Is Cloudflare D1 free? There is a free tier on the Workers Free plan: 5 million rows read and 100,000 rows written per day, with 5 GB of storage. Many small applications run entirely within these limits. The Workers Paid plan raises the included amounts substantially.
How is D1 priced? You pay for rows read and written, not for uptime. On the Paid plan the first 25 billion rows read and 50 million rows written per month are included, then it is $0.001 per million reads and $1.00 per million writes, with storage at $0.75 per GB-month beyond the first 5 GB. There are no egress fees.
What is the difference between D1 and KV? KV is a key-value store optimised for fast reads of single keys, ideal for caching and configuration. D1 is a relational SQL database for structured data you want to query with joins, indexes and transactions. Use KV for lookups by key, D1 for relational queries.
Can D1 replace Postgres? For many small to medium applications, yes, because SQLite covers the common relational needs. For very large datasets, heavy concurrent write workloads, or Postgres-specific extensions, a dedicated Postgres database connected from your Worker (accelerated by Hyperdrive) is the better fit.
How do I query D1 from a Worker? Bind the database in your wrangler configuration and access it through env.DB. Use prepared statements with bound parameters, for example env.DB.prepare(“SELECT … WHERE id = ?”).bind(id).all(), which keeps queries safe from SQL injection.
Comments