Monitor Cloudflare Workers Cron Triggers
A Worker’s scheduled handler runs on Cloudflare’s network with no server to
watch. When it starts throwing, hits a limit, or the trigger is removed in a
deploy, nothing tells you. A heartbeat check does:
the Worker reports to Pingstack after its work succeeds, and Pingstack alerts
you when that report doesn’t arrive on time.
1. Create a heartbeat check that matches the trigger
Section titled “1. Create a heartbeat check that matches the trigger”Find the cron trigger in your Wrangler configuration:
[triggers]crons = ["0 2 * * *"]Create a heartbeat check and set its schedule to Cron with the same expression. Cron Triggers run in UTC, so choose UTC in Pingstack. See schedules and timezones.
Set a grace period that covers the handler’s runtime plus a few minutes. Attach a notification group.
2. Store the ping URL as a secret
Section titled “2. Store the ping URL as a secret”Copy the location’s ping URL from the check in Pingstack and add it as a Worker secret. Wrangler prompts for the value without echoing it:
npx wrangler secret put PINGSTACK_PING_URL3. Report at the end of the scheduled handler
Section titled “3. Report at the end of the scheduled handler”export default { async scheduled(controller, env, ctx) { ctx.waitUntil(runAndReport(env)); },};
async function runAndReport(env) { await runNightlyJob(env);
try { await fetch(env.PINGSTACK_PING_URL, { signal: AbortSignal.timeout(10_000) }); } catch (error) { console.warn(`Pingstack heartbeat failed: ${error.name}`); }}The report runs only after runNightlyJob resolves, so a run that throws
sends nothing and Pingstack alerts you when the deadline passes.
ctx.waitUntil keeps the Worker alive until the report finishes.
If one Worker has several triggers, check controller.cron to tell them apart
and report each to its own check.
4. Confirm the first report
Section titled “4. Confirm the first report”Test the handler locally with Wrangler, which exposes a route that fires the scheduled event:
npx wrangler dev --test-scheduledcurl "http://localhost:8787/__scheduled?cron=0+2+*+*+*"For local runs, put the ping URL in .dev.vars, which Wrangler reads and you
shouldn’t commit. Then deploy, and after the next scheduled run, open the
check in Pingstack and confirm the location received a heartbeat and shows a
next deadline. The Worker’s Settings › Triggers page in the Cloudflare
dashboard shows its cron triggers.
Optional: report a number
Section titled “Optional: report a number”Append a count the job produces as a numeric value:
const processed = await runNightlyJob(env);const target = new URL(env.PINGSTACK_PING_URL);target.searchParams.set('value', String(processed));await fetch(target, { signal: AbortSignal.timeout(10_000) });Set warning and critical thresholds on the check, so a run that succeeds but processes nothing still raises an alert.
Why a healthy Worker might still alert
Section titled “Why a healthy Worker might still alert”- Trigger changes. Changes to cron triggers can take several minutes to take effect after a deploy.
- CPU or duration limits. A handler that exceeds its plan’s limits is stopped before it reports. Check the Worker’s logs.
- Removed trigger. Deploying a configuration without the
cronsentry removes the trigger. Pingstack alerts, which is the point.
If alerts still don’t match what you expect, see troubleshooting monitoring.