Cloudflare Workers Cron Triggers: Schedule Serverless Functions
Cloudflare Workers Cron Triggers let you run serverless functions on a schedule without managing any infrastructure. Define a cron expression in your wrangler.toml, write a scheduled event handler, and Cloudflare runs your code on its global edge network. This guide covers setup, syntax, free tier limits, and when an external scheduler like CronJobPro is a better fit.
What Are Cron Triggers?
Cron Triggers are a Cloudflare Workers feature that lets you invoke a Worker on a recurring schedule. Unlike normal Workers that respond to HTTP requests, a Cron Trigger fires the scheduled event handler at the times you define. The Worker runs on Cloudflare's edge network, so there is no server to manage, no cold start to worry about, and no infrastructure cost beyond the Workers plan.
Common use cases include cleaning up KV or D1 data, sending digest emails, syncing data between APIs, generating reports, and refreshing cache. Anything you can do in a Worker (fetch external APIs, read/write KV, query D1 databases, call R2 storage) you can schedule with a Cron Trigger.
Setup: wrangler.toml + Scheduled Handler
Setting up a Cron Trigger takes two steps: define the schedule in your wrangler.toml and implement the scheduled event handler in your Worker code.
Step 1: Configure wrangler.toml
# wrangler.toml
name = "my-scheduled-worker"
main = "src/index.ts"
compatibility_date = "2026-03-01"
[triggers]
crons = [
"*/5 * * * *", # Every 5 minutes
"0 9 * * 1-5", # Weekdays at 9 AM UTC
"0 0 1 * *" # First day of every month at midnight UTC
]Step 2: Implement the Scheduled Handler
// src/index.ts
export default {
async scheduled(
controller: ScheduledController,
env: Env,
ctx: ExecutionContext
): Promise<void> {
// controller.scheduledTime - the time this trigger was supposed to fire
// controller.cron - the cron expression that triggered this invocation
switch (controller.cron) {
case "*/5 * * * *":
await cleanupExpiredSessions(env);
break;
case "0 9 * * 1-5":
await sendDailyDigest(env);
break;
case "0 0 1 * *":
await generateMonthlyReport(env);
break;
}
},
// You can still handle HTTP requests in the same Worker
async fetch(request: Request, env: Env): Promise<Response> {
return new Response("OK");
},
};
async function cleanupExpiredSessions(env: Env) {
// Example: delete expired keys from KV
const list = await env.SESSIONS.list();
for (const key of list.keys) {
if (key.expiration && key.expiration < Date.now() / 1000) {
await env.SESSIONS.delete(key.name);
}
}
}
async function sendDailyDigest(env: Env) {
// Example: call an external email API
await fetch("https://api.example.com/send-digest", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${env.API_KEY}`,
},
body: JSON.stringify({ type: "daily-digest" }),
});
}
async function generateMonthlyReport(env: Env) {
// Example: query D1 and store result in R2
const result = await env.DB.prepare(
"SELECT COUNT(*) as total FROM orders WHERE created_at >= date('now', '-1 month')"
).first();
await env.REPORTS.put(
`report-${new Date().toISOString().slice(0, 7)}.json`,
JSON.stringify(result)
);
}Step 3: Deploy and Test
# Deploy the Worker with Cron Triggers
npx wrangler deploy
# Test locally with the --test-scheduled flag
npx wrangler dev --test-scheduled
# Then visit http://localhost:8787/__scheduled to triggerCron Syntax Support
Cloudflare Cron Triggers use standard five-field cron expression syntax: minute, hour, day of month, month, and day of week. All times are in UTC.
| Expression | Meaning |
|---|---|
| * * * * * | Every minute |
| */15 * * * * | Every 15 minutes |
| 0 */2 * * * | Every 2 hours |
| 0 9 * * 1-5 | Weekdays at 9:00 AM UTC |
| 0 0 1,15 * * | 1st and 15th of every month at midnight |
Use the CronJobPro cron generator to build and validate expressions visually before adding them to your wrangler.toml.
Free Tier: 5 Cron Triggers
The Cloudflare Workers free plan includes up to 5 Cron Triggers and 100,000 Worker requests per day. Each scheduled invocation counts as one request. For most small projects, this is more than enough. However, the free plan limits CPU time to 10ms per invocation, which can be restrictive for heavier tasks.
| Plan | Cron Triggers | CPU Time | Price |
|---|---|---|---|
| Free | 5 | 10ms per invocation | $0 |
| Paid ($5/mo) | 250 | 30s per invocation | $5/month |
Limitations
Cron Triggers are powerful for lightweight scheduled tasks, but they have constraints you should understand before committing:
- •1-minute minimum interval: You cannot schedule tasks more frequently than once per minute. For sub-minute scheduling, see our guide on running jobs every 30 seconds.
- •UTC only: All cron expressions run in UTC. There is no timezone configuration. You must manually convert local times to UTC and update your expressions when DST changes.
- •No built-in monitoring: There is no dashboard showing execution history, success/failure rates, or duration trends. Failed invocations appear in Workers analytics, but there is no alerting mechanism.
- •Workers-only: Cron Triggers can only invoke Cloudflare Workers. You cannot directly schedule an HTTP call to an external endpoint — you need a Worker as a proxy.
- •CPU time limits: The free plan gives you only 10ms of CPU time. Even the paid plan caps at 30 seconds. Long-running tasks are not suitable for Workers Cron Triggers.
- •No retry configuration: If a scheduled invocation fails, Cloudflare does not retry it. The next execution happens at the next scheduled time. You must implement your own retry logic within the handler.
When to Use Workers Cron vs CronJobPro
Use Cloudflare Workers Cron Triggers when:
- •Your scheduled logic is lightweight (under 10ms CPU on free, 30s on paid)
- •You are already using Cloudflare Workers, KV, D1, or R2
- •You need fewer than 5 scheduled tasks and the free plan covers your needs
- •UTC scheduling is acceptable for your use case
Use CronJobPro when:
- •You need to call HTTP endpoints on any infrastructure (not just Workers)
- •You need timezone-aware scheduling with DST handling
- •You need built-in monitoring with execution history and failure alerts
- •You want dead man's switch monitoring for jobs triggered elsewhere
- •Your tasks take longer than 30 seconds to complete
Many teams use both: Cloudflare Workers Cron for lightweight edge tasks and CronJobPro for scheduling and monitoring their backend jobs across all environments.
Frequently Asked Questions
How many Cron Triggers can I have on the Cloudflare Workers free plan?
The Cloudflare Workers free plan allows up to 5 Cron Triggers per account. Each trigger can have its own cron expression and handler. The paid Workers plan ($5/month) increases this to 250 Cron Triggers.
What is the minimum interval for Cloudflare Workers Cron Triggers?
The minimum interval is 1 minute (using the cron expression * * * * *). Cloudflare does not support sub-minute scheduling. If you need to run tasks more frequently, consider using Durable Objects alarms or an external scheduler.
Can Cloudflare Cron Triggers call external APIs?
Yes. Inside your scheduled event handler, you can use the fetch API to call any external HTTP endpoint. The Worker has up to 30 seconds of CPU time (paid plan) to complete the request.
How do I test Cron Triggers locally?
Use the Wrangler CLI with the --test-scheduled flag. Run npx wrangler dev --test-scheduled, then visit http://localhost:8787/__scheduled to trigger the scheduled handler without waiting for the actual schedule.
Do Cloudflare Cron Triggers support timezone configuration?
No. Cron Triggers always run in UTC. There is no timezone configuration option. If you need timezone-aware scheduling, calculate the UTC equivalent of your local time or use an external scheduler like CronJobPro that supports timezone-aware scheduling.
Related Articles
Need More Than Cron Triggers?
CronJobPro schedules HTTP calls to any endpoint with built-in monitoring, timezone support, and alerting. Free for up to 5 monitors.
Start Monitoring Free