My side project died on a Tuesday. UptimeRobot's free tier checks every 5 minutes, so by the time the alert hit my inbox, 47 users had already bounced off a 502. The $7/month Pro plan checks every 60 seconds — which felt absurd for what's literally a cron job doing an HTTP GET.
So I built my own on Cloudflare Workers' free tier. Here's the math and the code.
The comparison
| UptimeRobot Free | UptimeRobot Pro | My CF Worker | |
|---|---|---|---|
| Check interval | 5 min | 60 sec | 30 sec |
| Monitors | 50 | 50 | Unlimited |
| Price/month | $0 | $7 | $0 |
| Status page | Yes | Yes | Yes (Worker route) |
Cloudflare Workers free tier gives 100,000 requests/day. A check every 30 seconds across 8 endpoints = 23,040 requests/day. I'm using 23% of free quota.
The code (whole thing, 40 lines)
export default {
async scheduled(event, env, ctx) {
const targets = [
"https://api.myapp.com/health",
"https://myapp.com",
"https://status.myapp.com",
];
for (const url of targets) {
const start = Date.now();
try {
const res = await fetch(url, { cf: { cacheTtl: 0 } });
const ms = Date.now() - start;
if (!res.ok) await alert(env, url, res.status, ms);
await env.KV.put(`status:${url}`,
JSON.stringify({ ok: res.ok, ms, at: Date.now() }),
{ expirationTtl: 86400 });
} catch (e) {
await alert(env, url, "DOWN", Date.now() - start);
}
}
},
async fetch(request, env) {
// Public status page: read KV and render
const targets = ["https://api.myapp.com/health", "https://myapp.com"];
const rows = await Promise.all(targets.map(async u =>
[u, await env.KV.get(`status:${u}`)]));
return new Response(rows.map(([u, s]) => {
const d = JSON.parse(s || "{}");
return `${d.ok ? "🟢" : "🔴"} ${u} — ${d.ms ?? "?"}ms`;
}).join("\n"), { headers: { "content-type": "text/plain" } });
}
};
wrangler.toml:
[triggers]
crons = ["* * * * *"] # every minute; use */2 for 30s via two cron entries
Alerting goes through a free Discord webhook — 5 lines of fetch, unlimited messages. Total setup time: about an hour, most of it reading the KV docs.
What I gave up
- No fancy incident management (PagerDuty-style escalation). For a side project: fine.
- No historical graphs unless I add them. Workers Analytics Engine (also free) covers this if I ever care.
- Multi-region checks: UptimeRobot checks from ~10 locations, mine checks from one CF edge. Hasn't mattered yet — if Cloudflare's edge can't reach my origin, that's the failure I care about.
I prototyped the Worker with MonkeyCode in one afternoon — it generated the KV read/write boilerplate and the cron config on the first try: https://ly.cyberserval.tech/iIETXiF
Six weeks in: 3 real outages caught, 0 false positives, $0 spent. The 30-second granularity caught a flapping deploy that UptimeRobot's 5-minute window would have missed entirely.
Are you paying for uptime monitoring, or did you roll your own too? What's your alerting channel — Discord, Telegram, or old-school email?
Top comments (0)