Originally published at guushu.com/notes. I keep the original updated, so this copy may lag.
Eight public Cloudflare bill spikes turned up in 2026 threads on Cloudflare Community and Hacker News. Seven are D1 rows read. One is a Durable Objects alarm loop.
Every fix was one index or one guard on a reschedule. The fix was never the hard part. Nobody saw the meter move for days.
What goes in the table: thread title, date, product, amount. No usernames, no quotes, no links to the threads. Amounts are what the poster said, rounded. Where they didn't say, the table says so.
Last checked 2026-09-15. I add rows when I find them and bump the updated date at the top.
The 8 incidents, by product and amount
| Date | Source | Product | Amount | What moved |
|---|---|---|---|---|
| 04-16 | HN | Durable Objects | about $34,895 over 8 days | Alarm re-scheduled itself in a loop; zero users |
| 06-29 | CF Community | D1 | about $800 | Sitemap of 380,000 pages crawled; every page a full-table read |
| 08-03 | CF Community | D1 | about $176 | Rows read overage on a small site |
| 08-10 | CF Community | D1 | about $4,115 | $5 subscription; D1 rows read ran for a month |
| 08-25 | CF Community | Workers / D1 | not stated | "Accidentally incurred a large billable usage" |
| 09-01 | CF Community | D1 | invoice about $1,481, credit asked about $976 | 1.476 trillion rows read |
| 09-03 | CF Community | D1 | not stated | Overage after a data migration; backfill loop |
| 09-11 | CF Community | D1 | quota hit, no invoice yet | Rows read shown as 235k in one view and 11.5M in another |
Seven D1, one Durable Object. The smallest stated amount is $176. The largest is $34,895.
D1 pricing is $0.001 per million rows read. So the 09-01 invoice is just what 1.476 trillion rows cost per million rows.
Why does D1 bill rows read instead of requests?
D1 meters rows scanned, not queries. A SELECT ... WHERE slug = ? on a table with no index on slug scans the whole table on every request. Take 10,000 rows and 100 requests a minute. After one day, that's 1.4 billion rows for one small site, and the query count never moved.
Every D1 row in the table is that meter. The 09-03 migration is the same thing in a different shape: a backfill ran SELECT inside a loop over a table that had just grown.
Durable Objects alarm loops that never sleep
The Durable Objects case is an alarm handler that sets the next alarm at the end of every run. Failed runs too.
Durable Objects requests are metered per million on Workers Paid. A loop with no sleep and nobody watching runs until the invoice shows up. Eight days, about $34,895, zero users.
A 10,000x jump that nobody saw for days
None of these are Cloudflare being expensive. In every thread the metric jumped 10,000x or more, and the first sign was the invoice or a quota email.
Cloudflare's Budget alerts arrived in 2026-04 and have been on by default for pay-as-you-go accounts since 2026-07. They send one email at the threshold. The docs call them informational only. They don't pause or cap anything, so someone still has to log in and act.
Where to see D1 rows read and DO requests before the invoice
- Dashboard: Workers & Pages → D1 → your database → Metrics. "Rows read" is per database, per day. You're looking for a flat line that suddenly isn't.
- GraphQL Analytics: dataset
d1AnalyticsAdaptiveGroups, dimensiondatabaseId, sum ofreadQueriesandrowsReadover a date range. Same numbers as the dashboard, but you can script it. - Durable Objects: Workers & Pages → Durable Objects → Metrics. Requests and alarm invocations per namespace. One namespace climbing on an otherwise flat account is the loop.
The total doesn't tell you much. The check worth automating is whether today is more than 10x the last seven days.
How I now guard my own D1 and Durable Objects usage
- Every D1 table I query by a non-primary-key column gets an index on that column. New queries go through
EXPLAIN QUERY PLANfirst, and anything that saysSCANgets an index or a rewrite. My own D1 is about 15,000 rows, so this is prevention, not a war story. The smallest example is thereceived_atindex in my Cloudflare Email Worker inbox, 30 lines that store mail in D1. - I don't run Durable Objects yet. When I do, the re-schedule goes in the success path only, with a retry counter on the object.
- Since 2026-09-15 the "10x the last seven days" check has run on my own account as a small read-only Worker, usage-guard-collector (MIT). Every 6 hours it reads the GraphQL Analytics API into a D1 table and flags any meter that's 10x its 7-day median. The hosted version linked below is still a waitlist page.
What this page can't tell you: eight rows isn't a dataset, the amounts are self-reported, and I only read English-language threads. If you know a public thread that belongs here, the address in the footer works.
I'm building a small watcher for exactly this gap. If a bill has surprised you, the one-question form is at guard.guushu.com.
Original, with any later corrections: guushu.com/notes/cloudflare-bill-incidents-2026/
Top comments (0)