Skip to content
LastPing
GUIDE · CLOUDFLARE WORKERS · CRON TRIGGERS · FREE

Monitor Cloudflare Workers Cron Triggers: detect failed scheduled runs

A Cloudflare Cron Trigger invokes your Worker's scheduled() handler on a cron schedule. If that handler throws (an unhandled exception, a failed subrequest, a timeout), Cloudflare marks the invocation as errored in Workers Analytics, but sends you nothing. If the trigger silently stops firing, you also get nothing. Add one fetch ping inside the handler and LastPing catches every failure and every missed run in seconds.

Your assistant can do this for you

Monitor my Cloudflare Workers cron triggers with LastPing

On this page
  1. Setup: one fetch in your scheduled() handler
  2. What this catches
  3. Questions people ask

Setup: one fetch in your scheduled() handler

Ping on success, ping /fail in the catch block. Works from any Worker runtime.

Create a heartbeat monitor in LastPing

In the console, create a monitor with the same cron schedule as your trigger. Cloudflare crons run in UTC, so set the monitor's timezone to UTC. Copy the ping URL.

https://ping.lastping.dev/<your-monitor-id>

Declare the cron in wrangler.toml

Set your schedule and add the monitor id as a Worker variable (or a secret via wrangler secret put):

# wrangler.toml
name = "nightly-job"
main = "src/index.js"

[triggers]
crons = ["0 3 * * *"]   # 03:00 UTC daily

[vars]
LASTPING_ID = "<your-monitor-id>"

Ping from the scheduled() handler

Ping on success; fetch the /fail path on error, then re-throw so the invocation still shows as errored in Cloudflare too:

// src/index.js
export default {
  async scheduled(event, env, ctx) {
    const ping = `https://ping.lastping.dev/${env.LASTPING_ID}`;
    try {
      await doWork(env);
      await fetch(ping);                         // success
    } catch (err) {
      await fetch(`${ping}/fail`, {              // failure
        method: "POST",
        body: String(err && err.stack || err),
      });
      throw err;
    }
  },
};

Use waitUntil for the ping if you return early

If your handler returns before the ping resolves, wrap it in ctx.waitUntil(fetch(ping)) so the runtime keeps the request alive until the ping completes.

What this catches

  • Handler throws: the success ping is skipped (or the /fail ping fires); silence in the grace window opens an incident.
  • Trigger stops firing: a disabled or removed Cron Trigger produces no ping → incident opens.
  • Silent no-op: validate the work actually happened and only ping on confirmed success.
  • Recovery: the next successful invocation closes the incident automatically.

Also on Fly.io? See Fly.io scheduled machines. On Render? See Render cron jobs. For the general pattern, see Monitor cron jobs, and Node.js scripts for the runtime details.

Questions people ask

Front-loaded answers: the most important fact first.

  • Does Cloudflare alert when a Cron Trigger fails?

    No. When a scheduled() handler throws, Cloudflare records the invocation as errored in Workers Analytics and the dashboard, but sends no notification. If the Cron Trigger stops firing entirely, nothing alerts you. A dead-man's-switch ping catches both: it expects a check-in on every scheduled run and opens an incident when one doesn't arrive.

  • How do I add monitoring to a Cloudflare Worker cron?

    Inside the scheduled() handler, await fetch('https://ping.lastping.dev/<id>') after the work succeeds, and fetch the /fail path in a catch block. Create a LastPing monitor with the same cron schedule. If no ping arrives within the grace window, an incident opens.

  • What timezone do Cloudflare Cron Triggers use?

    Always UTC: Cloudflare Cron Triggers are always evaluated in UTC. There is no per-Worker timezone setting, so write your cron expression in UTC and set the LastPing monitor to UTC as well.

  • Will the ping count against my subrequest limit?

    The ping is a single outbound fetch, which counts as one subrequest, negligible against the per-invocation limit. Use ctx.waitUntil() if the handler might return before the ping resolves.

  • Is this free?

    Yes. LastPing is free for individuals with no monitor cap. See vs Healthchecks.io and vs Cronitor.

FREE FOR INDIVIDUALS · FULLY HOSTED

A Worker cron that throws tells no one. One fetch changes that.

Ping from your scheduled() handler. First alert in under a minute, and LastPing monitors itself the same way.