Blog

/

Cloudflare Workers email cost: every line item, in plain numbers

The Cloudflare email stack is four different metered products that get billed independently, and the easiest way to be surprised by your invoice is to assume one of them is free. This post walks through Workers per-request + CPU time, Cloudflare Email Services per-email, D1 reads/writes/storage, and R2 storage + operations, with the actual numbers and the common ways the bill drifts.

Workers: per-request and CPU time

The Workers line item is the one most engineers underestimate because it is two separate meters in a trench coat. The first meter is requests: 100,000 per day on the free plan, 10 million per month included on the $5/month Workers Paid plan, then $0.30 per additional million. The second meter is CPU time, measured in milliseconds: 10 million CPU-milliseconds per month included on the paid plan, then $0.02 per additional million. On Workers Unbound (no duration cap, no 10ms CPU cap) it is $0.15 per million requests plus $0.024 per million wall-clock milliseconds.

For an email pipeline, the per-request meter is the friendlier one. A single inbound message is one request, a single outbound message is another, a webhook fan-out of ten recipients is eleven requests (the original send plus ten outbound deliveries). The CPU meter is the one that catches teams by surprise — MIME parsing on a 100 KB envelope can spend 50–80 ms of CPU on a cold start, and a busy inbox can multiply that by 10,000 events per day before anyone notices. The mitigation is straightforward: warm the Worker, cache the parser results in module scope, and move heavy work to a Queue consumer rather than the request hot path.

Email Services: per-email, plus what is included free

Cloudflare Email Services is the simplest line item to model because there is one number. As of mid-2026 the metered rate is roughly $0.09 per 1,000 emails sent, with the first 3,000 emails per month included free on every zone. There is no per-domain fee, no commitment tier, no dedicated-IP upsell, and no separate log retention product. The DKIM, SPF, and the MX records are all part of the same primitive.

The Email Services price includes the signing work but it does not include the storage of the sent message body — that is your responsibility, and the natural place to put it is D1 (for the searchable row) plus R2 (for attachments). The cost line to budget is therefore not just the $0.09 per 1,000 but also the row write to D1 and the put to R2 for any attachment. The combined per-message cost on a Worker that writes one D1 row and zero R2 objects is roughly $0.09 plus the Workers CPU for the write. If you are sending messages with attachments, see the R2 section below.

D1: reads, writes, and storage, all separately

D1 is the most counter-intuitive line item on the bill because it has three independent meters. The first is row reads: 5 million per day on the free plan, 50 billion per month on the paid plan, then $0.001 per additional million. The second is row writes: 100,000 per day on the free plan, 50 million per month on the paid plan, then $1.00 per additional million. The third is storage: 5 GB included on the free plan, 10 GB included on the paid plan, then $0.75 per additional GB-month.

For an email product the meter that grows fastest is writes — every inbound message, every reply, every read-receipt event, every tag update is a write. A team handling 100,000 inbound messages per day with a few updates per message will burn through the 50 million monthly writes in two weeks. The mitigations are batching writes to D1 (Drizzle handles this cleanly), denormalizing so a single write covers multiple read paths, and keeping high-churn data (like per-event audit logs) in R2 or Workers KV rather than D1. Reads are almost never the bottleneck unless you are running analytics queries on the email rows — those should land in a separate read-replica or be served from KV.

R2: storage and operations, no egress

R2 is the pleasant surprise on the Cloudflare invoice because egress is free. The meters that do exist are storage ($0.015 per GB-month), Class A operations ($4.50 per million — these are writes, lists), and Class B operations ($0.36 per million — these are reads). The free tier includes 10 GB of storage, 10 million Class A operations per month, and 100 million Class B operations per month, which is generous enough that most small email products never leave the free tier on R2.

For attachments, the natural shape is: write the attachment to R2 on inbound (one Class A operation), store the R2 key on the email row in D1, and serve the download through a Worker that streams from R2 (one Class B operation per download). At realistic attachment volumes — a few hundred MB per day for a small support desk — the bill stays well under $1 per month. Where teams get caught is storing cold attachments that nobody reads: an R2 bucket of decade-old PDFs nobody has opened in three years still costs the storage meter. The mitigation is a lifecycle policy that moves anything older than 12 months to Infrequent Access storage.

Putting the four meters together

For a representative small team — 50,000 inbound messages per month, 30,000 outbound, 100,000 read-events, 200 MB of attachments — the monthly Cloudflare invoice shakes out roughly like this: Workers Paid $5 (covers the request volume), Email Services about $7 ($0.09 per 1,000 × 77,000 sends above the 3,000 free tier), D1 about $0.50 on the row writes plus effectively nothing on reads and storage, R2 effectively nothing within the free tier. Total around $13 per month for a non-trivial email pipeline.

The same shape at 10× the volume — 500,000 inbound, 300,000 outbound — puts the bill at about $70 per month, almost all of it Email Services per-email. The Workers, D1, and R2 portions scale sub-linearly because each of their meters has a steep free or included tier. The transactional email pricing post has the side-by-side against Resend, Mailgun, SES, and Postmark for comparison.

How to keep the bill boring

The four rules that keep a Cloudflare email bill predictable: warm the Worker so MIME parsing is not on the cold path, batch writes to D1 so you do not pay for one row update per event, attach a lifecycle policy to the R2 bucket so cold attachments leave the hot storage tier, and set a Workers Paid budget alert at roughly 2× your expected spend. The fifth rule, the one most teams forget, is to actually read the monthly Cloudflare invoice once — the per-meter breakdown is exported as CSV from the dashboard and it makes the cost shape obvious in about ten minutes.

Want the same stack, with the cost baked into a flat plan?