Uptime Monitoring

Zero KV Writes While Healthy — the Arithmetic of a Free-Forever Uptime Monitor

By Josh Comstock · Updated July 2026 · ~6 min read

A wooden abacus with rows of multicolored counting beads

We put Foghorn on GitHub yesterday — MIT licensed, one source file, the dead-man watchdog we wrote about back in June. The pitch over there is simple: one text when your server goes down, one when it's back, and it runs on Cloudflare's free tier forever.

"Free forever" gets thrown around a lot, and it usually means "free until you actually use it." So this post is the receipts — the quota arithmetic that makes the claim hold, the one design decision that makes the arithmetic work, and the honest edge of where the math runs out.

The Quota That Actually Bites

A watchdog that checks every minute runs 1,440 times a day. That number is the whole problem, and it's worth staring at next to Cloudflare's free-tier KV limits:

Quota (free tier, per day)LimitNaive monitorFoghorn, healthy
KV writes1,0001,4400
KV reads100,0001,440 per URL1,440 per URL

The reads are a non-story — 1,440 reads per URL against a 100,000 quota is a rounding error. The writes are the trap. A monitor that stores its state after every check — "still up, wrote it down; still up, wrote it down" — spends 1,440 writes against a 1,000 budget.

Over quota by breakfast.

At that point you've got two options: put a credit card on the account for a heartbeat diary nobody will ever read, or stop writing the diary.

Write Only the News

Here's the observation that fixes it: a server's status is boring almost all of the time, and "boring" is already in the database. If KV says the server is up and the check says it's still up, writing that down again is spending your scarcest quota to record that nothing happened. So Foghorn writes state only when something changes — up to down, down to up, a failure streak advancing toward the alert threshold. A healthy server does zero KV writes. Not few. Zero. A rough month with a couple of outages costs you a handful.

And that's the entire trick. No batching layer, no clever TTL games, no paid plan — just refusing to write anything that isn't news. The free tier stops being a constraint you manage and becomes one you can't realistically hit.

The honest edge of the math: it's the reads that scale with your watch list, at 1,440 per URL per day. Against 100,000 that's real headroom — call it seventy URLs before you'd even approach the ceiling. But if you're watching seventy URLs, let's be honest with each other: you've outgrown a pager and you want a real monitoring product, dashboards and all. For a handful of servers you actually own, the quotas may as well be infinite.

The Alert Is Part of the State

There's one more design decision that matters more than the quota math, and it's the one most homegrown monitors get wrong. Picture the failure: your server goes down, the Worker dutifully writes state = down to KV, then calls Twilio — and Twilio has a bad moment and rejects the send. Next minute, the Worker wakes up, reads KV, sees the state is already "down," and concludes there's no transition to announce. It will keep concluding that, minute after minute, for the entire outage.

The alarm rang in a room with nobody in it, then marked itself heard.

For a dead-man watchdog that's not an edge case — it's a broken contract. So in Foghorn, delivery is part of the state machine. The transition isn't committed to KV until at least one configured notifier — Twilio, or the webhook — actually accepts the message. If every notifier fails, the state doesn't change, and the next cron run sees the same transition still pending and tries again, minute after minute, until one delivers. An alert that didn't send didn't happen — so the Worker can't ever believe it told you something it didn't.

Cache-Busted, Because Cloudflare Is Good at Its Job

One quieter detail earns a mention. If the site you're watching sits behind Cloudflare — ours do — a plain fetch of your homepage can come back 200 from an edge cache while the origin behind it is a smoking crater. Your CDN's whole job is to keep serving when the origin can't, which makes it a beautiful thing to run a business on and a terrible thing to point a health check at. So every check Foghorn makes carries a cache-busting parameter, forcing it through to the origin. You're monitoring the server, not Cloudflare's memory of the server. And the check judges what comes back honestly: 2xx or 3xx is up; a 4xx, a 5xx, a ten-second timeout, or a refused connection is down.

Small Enough to Audit the Claim Yourself

Everything above lives in one TypeScript file with a vitest suite, which means you don't have to take my word for any of it — the state machine, the write-only-on-change rule, the delivery gate, all of it is a coffee's worth of reading. Clone it, drop in your URLs and a notifier, wrangler secret put the Twilio keys, and npx wrangler deploy. There's no server to host and nothing to renew, and the first Cloudflare bill will never come — the only line item in this whole setup is Twilio, at pennies per outage.

Built because our server needed watching and everything else wanted to be a product.

See how Foghorn works →

Related: A status page won't wake you up — why it's a dead-man alarm · Foghorn on GitHub.

← All articles