Cron Heartbeat
Cron Triggers reference: scheduled Worker runs with heartbeat metadata stored in KV - pattern for periodic edge jobs
A minimal reference for Cron Triggers: a scheduled() handler runs every 5 minutes and writes run metadata to Workers KV. An HTTP route reads that metadata back.
API Reference
GET /status
Returns metadata from the most recent scheduled run.
Example Request
curl "https://your-worker.workers.dev/status"Response Fields
lastRun string | null
ISO timestamp of the last cron execution.
lastCron string | null
Cron expression that fired (e.g. */5 * * * *).
runCount number
Total scheduled runs since deploy.
Example Response
{
"lastRun": "2025-06-20T12:00:00.000Z",
"lastCron": "*/5 * * * *",
"runCount": 12
}Before the first cron fires, lastRun and lastCron are null and runCount is 0.
Use Cases
- Learn how to implement a
scheduled()handler in Workers - Periodic health checks, cache warming, or cleanup jobs
- Background maintenance without external cron services
- Storing cron run history in KV for observability
Limitations
- Cron schedule is fixed in
wrangler.json; change and redeploy to adjust timing - Stores last-run metadata in KV only; not a full job scheduler
- Demonstration worker; no alerting or retry logic on failed runs
Use in your project
Copy these files into an existing Worker. Prefer Deployment to try the full experiment first. Source: apps/experiments/cron-heartbeat.
import type { Env } from "../types/env";import type { HeartbeatStatus } from "../types/status";import { STATUS_KEYS } from "../constants/keys";export async function getHeartbeatStatus(env: Env): Promise<HeartbeatStatus> { const [lastRun, lastCron, runCountRaw] = await Promise.all([ env.STATUS.get(STATUS_KEYS.lastRun), env.STATUS.get(STATUS_KEYS.lastCron), env.STATUS.get(STATUS_KEYS.runCount), ]); return { lastRun, lastCron, runCount: Number.parseInt(runCountRaw ?? "0", 10), };}export async function recordHeartbeat(env: Env, cron: string): Promise<HeartbeatStatus> { const current = await getHeartbeatStatus(env); const nextCount = current.runCount + 1; const lastRun = new Date().toISOString(); await Promise.all([ env.STATUS.put(STATUS_KEYS.lastRun, lastRun), env.STATUS.put(STATUS_KEYS.lastCron, cron), env.STATUS.put(STATUS_KEYS.runCount, String(nextCount)), ]); return { lastRun, lastCron: cron, runCount: nextCount, };}Deployment
Create KV namespace
Update wrangler.json with your KV namespace ID for the STATUS binding.
Check status
curl "https://your-worker.workers.dev/status"Local Development
cd apps/experiments/cron-heartbeat
npm install
npm run devTrigger a cron manually in local dev:
curl "http://localhost:8787/cdn-cgi/handler/scheduled"Configuration
wrangler.json declares:
- Cron trigger
*/5 * * * *(every 5 minutes) - KV binding
STATUSfor run metadata
Cloudflare Features Used
- Workers - Edge compute runtime
- Cron Triggers - scheduled Worker invocations
- Workers KV - persist run metadata across invocations