One scheduled() handler serves every cron; controller.cron says which

triggers.crons is an array; every expression calls the same scheduled(controller, env, ctx) in UTC. Test locally with wrangler dev --test-scheduled.

Queues & Crons

· Chapter

45

·

3

min read

The answer. A Worker becomes a scheduled job by exporting scheduled(controller, env, ctx) and listing cron expressions under triggers.crons. All expressions invoke the same handler; controller.cron holds the exact string that fired, so a switch on it routes work. Schedules are evaluated in UTC, support the usual five fields plus extensions like LW (last weekday), and take up to 15 minutes to propagate after a deploy. The runtime waits for the handler's promise (15-minute wall clock, 30 s of CPU for sub-hourly schedules), so a single await is enough; ctx.waitUntil is for running legs concurrently and for choosing which promise's failure marks the run as failed in the Past Events table. Locally, wrangler dev --test-scheduled exposes a route you curl to fire the handler on demand.

The pattern.

[triggers]
crons = ["*/15 * * * *", "0 8 * * *"]    # UTC; the deploy replaces whatever crons were live
export default {
  async scheduled(controller, env, ctx) {
    switch (controller.cron) {
      case "*/15 * * * *": await runQuarterHourLegs(env, ctx); break;
      case "0 8 * * *":    await sendDailyDigest(env); break;
    }
  },
} satisfies ExportedHandler<Env>;
npx wrangler dev --test-scheduled
curl "http://localhost:8787/cdn-cgi/local/scheduled?cron=*/15+*+*+*+*&format=json"   # {"outcome":"ok"}

Watch out.

  • crons = [] deletes every trigger on deploy; leaving the triggers key out keeps the deployed ones. Named environments do not inherit triggers at all.
  • Accounts get 5 Cron Triggers on Free and 250 on Paid, counted across Workers.
  • A cron run has no request, so nothing short-circuits a hung leg except the 15-minute wall clock; isolate legs and add a heartbeat.

Related: cron-one-tick-many-isolated-legs · cron-heartbeat-dead-mans-switch · wrangler-environments-inherit-nothing-that-binds