jackspace/claudeskillz/cloudflare-cron-triggers

Trust tier 3
  • community
Source check pending

Checking that the SKILL.md file is still in the repository.

Complete knowledge domain for Cloudflare Cron Triggers - scheduled execution of Workers using cron expressions for periodic tasks, maintenance jobs, and automated workflows. Use when: scheduling Workers to run periodically, adding cron triggers to Workers, configuring scheduled tasks, testing cron handlers, combining crons with Workflows, enabling Green Compute, handling multiple schedules, or encountering "scheduled handler not found", "cron expression invalid", "changes not propagating", "handler does not export", "timezone issues" errors. Keywords: cloudflare cron, cron triggers, scheduled workers, scheduled handler, periodic tasks, background jobs, scheduled tasks, cron expression, wrangler crons, scheduled event, green compute, workflow triggers, maintenance tasks, scheduled() handler, ScheduledController, UTC timezone

Install

npx vskill@latest install jackspace/claudeskillz/cloudflare-cron-triggers

SKILL.md

Cloudflare Cron Triggers

Status: Production Ready ✅ Last Updated: 2025-10-23 Dependencies: cloudflare-worker-base (for Worker setup) Latest Versions: wrangler@4.43.0, @cloudflare/workers-types@4.20251014.0


Quick Start (5 Minutes)

1. Add Scheduled Handler to Your Worker

src/index.ts:

typescript
export default {
async scheduled(
controller: ScheduledController,
env: Env,
ctx: ExecutionContext
): Promise<void> {
console.log('Cron job executed at:', new Date(controller.scheduledTime));
console.log('Triggered by cron:', controller.cron);
// Your scheduled task logic here
await doPeriodicTask(env);
},
};

Why this matters:

  • Handler must be named exactly scheduled (not scheduledHandler or onScheduled)
  • Must be exported in default export object
  • Must use ES modules format (not Service Worker format)
2. Configure Cron Trigger in Wrangler

wrangler.jsonc:

jsonc
{
"name": "my-scheduled-worker",
"main": "src/index.ts",
"compatibility_date": "2025-10-23",
"triggers": {
"crons": [
"0 * * * *" // Every hour at minute 0
]
}
}

CRITICAL:

  • Cron expressions use 5 fields: minute hour day-of-month month day-of-week
  • All times are UTC only (no timezone conversion)
  • Changes take up to 15 minutes to propagate globally
3. Test Locally
bash
# Enable scheduled testing
npx wrangler dev --test-scheduled
# In another terminal, trigger the scheduled handler
curl "http://localhost:8787/__scheduled?cron=0+*+*+*+*"
# View output in wrangler dev terminal

Testing tips:

  • /__scheduled endpoint is only available with --test-scheduled flag
  • Can pass any cron expression in query parameter
  • Python Workers use /cdn-cgi/handler/scheduled instead
4. Deploy
bash
npm run deploy
# or
npx wrangler deploy

After deployment:

  • Changes may take up to 15 minutes to propagate
  • Check dashboard: Workers & Pages > [Your Worker] > Cron Triggers
  • View past executions in Logs tab

Cron Expression Syntax

Five-Field Format
* * * * *
│ │ │ │ │
│ │ │ │ └─── Day of Week (0-6, Sunday=0)
│ │ │ └───── Month (1-12)
│ │ └─────── Day of Month (1-31)
│ └───────── Hour (0-23)
└─────────── Minute (0-59)
Special Characters
CharacterMeaningExample
*Every* * * * * = every minute
,List0,30 * * * * = every hour at :00 and :30
-Range0 9-17 * * * = every hour from 9am-5pm
/Step*/15 * * * * = every 15 minutes
Common Patterns
bash
# Every minute
* * * * *
# Every 5 minutes
*/5 * * * *
# Every 15 minutes
*/15 * * * *
# Every hour at minute 0
0 * * * *
# Every hour at minute 30
30 * * * *
# Every 6 hours
0 */6 * * *
# Every day at midnight (00:00 UTC)
0 0 * * *
# Every day at noon (12:00 UTC)
0 12 * * *
# Every day at 3:30am UTC
30 3 * * *
# Every Monday at 9am UTC
0 9 * * 1
# Every weekday at 9am UTC
0 9 * * 1-5
# Every Sunday at midnight UTC
0 0 * * 0
# First day of every month at midnight UTC
0 0 1 * *
# Twice a day (6am and 6pm UTC)
0 6,18 * * *
# Every 30 minutes during business hours (9am-5pm UTC, weekdays)
*/30 9-17 * * 1-5

CRITICAL: UTC Timezone Only

  • All cron triggers execute on UTC time
  • No timezone conversion available
  • Convert your local time to UTC manually
  • Example: 9am PST = 5pm UTC (next day during DST)

ScheduledController Interface

typescript
interface ScheduledController {
readonly cron: string; // The cron expression that triggered this execution
readonly type: string; // Always "scheduled"
readonly scheduledTime: number; // Unix timestamp (ms) when scheduled
}
Properties
controller.cron (string)

The cron expression that triggered this execution.

typescript
export default {
async scheduled(controller: ScheduledController, env: Env): Promise<void> {
console.log(`Triggered by: ${controller.cron}`);
// Output: "Triggered by: 0 * * * *"
},
};

Use case: Differentiate between multiple cron schedules (see Multiple Cron Triggers pattern).

controller.type (string)

Always returns "scheduled" for cron-triggered executions.

typescript
if (controller.type === 'scheduled') {
// This is a cron-triggered execution
}
controller.scheduledTime (number)

Unix timestamp (milliseconds since epoch) when this execution was scheduled to run.

typescript
export default {
async scheduled(controller: ScheduledController): Promise<void> {
const scheduledDate = new Date(controller.scheduledTime);
console.log(`Scheduled for: ${scheduledDate.toISOString()}`);
// Output: "Scheduled for: 2025-10-23T15:00:00.000Z"
},
};

Note: This is the scheduled time, not the actual execution time. Due to system load, actual execution may be slightly delayed (usually <1 second).


Execution Context

typescript
export default {
async scheduled(
controller: ScheduledController,
env: Env,
ctx: ExecutionContext // ← Execution context
): Promise<void> {
// Use ctx.waitUntil() for async operations that should complete
ctx.waitUntil(logToAnalytics(env));
},
};
ctx.waitUntil(promise: Promise<any>)

Extends the execution context to wait for async operations to complete after the handler returns.

Use cases:

  • Logging to external services
  • Analytics tracking
  • Cleanup operations
  • Non-critical background tasks
typescript
export default {
async scheduled(controller: ScheduledController, env: Env, ctx: ExecutionContext): Promise<void> {
// Critical task - must complete before handler exits
await processData(env);
// Non-critical tasks - can complete in background
ctx.waitUntil(sendMetrics(env));
ctx.waitUntil(cleanupOldData(env));
ctx.waitUntil(notifySlack({ message: 'Cron completed' }));
},
};

Important: First waitUntil() that fails will be reported as the status in dashboard logs.


Integration Patterns

1. Standalone Scheduled Worker

Best for: Workers that only run on schedule (no HTTP requests)

typescript
// src/index.ts
interface Env {
DB: D1Database;
MY_BUCKET: R2Bucket;
}
export default {
async scheduled(
controller: ScheduledController,
env: Env,
ctx: ExecutionContext
): Promise<void> {
console.log('Running scheduled maintenance...');
// Database cleanup
await env.DB.prepare('DELETE FROM sessions WHERE expires_at < ?')
.bind(Date.now())
.run();
// Generate daily report
const report = await generateDailyReport(env.DB);
// Upload to R2
await env.MY_BUCKET.put(
`reports/${new Date().toISOString().split('T')[0]}.json`,
JSON.stringify(report)
);
console.log('Maintenance complete');
},
};

2. Combined with Hono (Fetch + Scheduled)

Best for: Workers that handle both HTTP requests and scheduled tasks

typescript
// src/index.ts
import { Hono } from 'hono';
interface Env {
DB: D1Database;
}
const app = new Hono<{ Bindings: Env }>();
// Regular HTTP routes
app.get('/', (c) => c.text('Worker is running'));
app.get('/api/stats', async (c) => {
const stats = await c.env.DB.prepare('SELECT COUNT(*) as count FROM users').first();
return c.json(stats);
});
// Export both fetch handler and scheduled handler
export default {
// Handle HTTP requests
fetch: app.fetch,
// Handle cron triggers
async scheduled(
controller: ScheduledController,
env: Env,
ctx: ExecutionContext
): Promise<void> {
console.log('Cron triggered:', controller.cron);
// Run scheduled task
await updateCache(env.DB);
// Log completion
ctx.waitUntil(logExecution(controller.scheduledTime));
},
};

Why this pattern:

  • One Worker handles both use cases
  • Share environment bindings
  • Reduce number of Workers to manage
  • Lower costs (one Worker subscription)

3. Multiple Cron Triggers

Best for: Different schedules for different tasks

wrangler.jsonc:

jsonc
{
"triggers": {
"crons": [
"*/5 * * * *", // Every 5 minutes
"0 */6 * * *", // Every 6 hours
"0 0 * * *" // Daily at midnight UTC
]
}
}

src/index.ts:

typescript
export default {
async scheduled(
controller: ScheduledController,
env: Env,
ctx: ExecutionContext
): Promise<void> {
// Route based on which cron triggered this execution
switch (controller.cron) {
case '*/5 * * * *':
// Every 5 minutes: Check system health
await checkSystemHealth(env);
break;
case '0 */6 * * *':
// Every 6 hours: Sync data from external API
await syncExternalData(env);
break;
case '0 0 * * *':
// Daily at midnight: Generate reports and cleanup
await generateDailyReports(env);
await cleanupOldData(env);
break;
default:
console.warn(`Unknown cron trigger: ${controller.cron}`);
}
},
};

CRITICAL:

  • Use exact cron expression match (whitespace sensitive)
  • Maximum 3 cron triggers per Worker (Free plan)
  • Standard/Paid plan supports more (check limits)

4. Accessing Environment Bindings

All Worker bindings available in scheduled handler:

typescript
interface Env {
// Databases
DB: D1Database;
// Storage
MY_BUCKET: R2Bucket;
KV_NAMESPACE: KVNamespace;
// AI & Vectors
AI: Ai;
VECTOR_INDEX: VectorizeIndex;
// Queues & Workflows
MY_QUEUE: Queue;
MY_WORKFLOW: Workflow;
// Durable Objects
RATE_LIMITER: DurableObjectNamespace;
// Secrets
API_KEY: string;
}
export default {
async scheduled(controller: ScheduledController, env: Env): Promise<void> {
// D1 Database
const users = await env.DB.prepare('SELECT * FROM users WHERE active = 1').all();
// R2 Storage
const file = await env.MY_BUCKET.get('data.json');
// KV Storage
const config = await env.KV_NAMESPACE.get('config', 'json');
// Workers AI
const response = await env.AI.run('@cf/meta/llama-3-8b-instruct', {
prompt: 'Summarize today\'s data',
});
// Send to Queue
await env.MY_QUEUE.send({ type: 'process', data: users.results });
// Trigger Workflow
await env.MY_WORKFLOW.create({ input: { timestamp: Date.now() } });
// Use secrets
await fetch('https://api.example.com/webhook', {
headers: { Authorization: `Bearer ${env.API_KEY}` },
});
},
};

5. Combining with Workflows

Best for: Multi-step, long-running tasks triggered on schedule

wrangler.jsonc:

jsonc
{
"triggers": {
"crons": ["0 2 * * *"] // Daily at 2am UTC
},
"workflows": [
{
"name": "daily-report-workflow",
"binding": "DAILY_REPORT"
}
]
}

src/index.ts:

typescript
interface Env {
DAILY_REPORT: Workflow;
}
export default {
async scheduled(controller: ScheduledController, env: Env): Promise<void> {
console.log('Triggering daily report workflow...');
// Trigger workflow with initial state
const instance = await env.DAILY_REPORT.create({
params: {
date: new Date().toISOString().split('T')[0],
reportType: 'daily-summary',
},
});
console.log(`Workflow started: ${instance.id}`);
},
};

Why use Workflows:

  • Workflows can run for hours (cron handlers have CPU limits)
  • Built-in retry and error handling
  • State persistence across steps
  • Better for complex, multi-step processes

Reference: Cloudflare Workflows Docs


6. Error Handling in Scheduled Handlers

This is the start of the file. Read the full SKILL.md on GitHub.

Security

  • Security scanPassed, 95 of 100Pattern checks on the skill's files.
  • Model reviewPassed

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Repository stars
12
Repository forks
4
7-day trend score
13

Stars and forks are for the whole GitHub repository, not this skill alone. Refreshed 3 Apr 2026, 6 months ago.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/jackspace/claudeskillz/cloudflare-cron-triggers/badge)](https://verified-skill.com/skills/jackspace/claudeskillz/cloudflare-cron-triggers)

Something wrong with this skill or its page? Tell us.