Skip to content

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:

wrangler.toml
[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.

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:

Terminal window
npx wrangler secret put PINGSTACK_PING_URL

3. Report at the end of the scheduled handler

Section titled “3. Report at the end of the scheduled handler”
src/index.js
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.

Test the handler locally with Wrangler, which exposes a route that fires the scheduled event:

Terminal window
npx wrangler dev --test-scheduled
curl "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.

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.

  • 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 crons entry removes the trigger. Pingstack alerts, which is the point.

If alerts still don’t match what you expect, see troubleshooting monitoring.