**Status**: Production Ready ✅
Configures Cloudflare Workers cron triggers for scheduled background tasks and automation.
npx claudepluginhub secondsky/claude-skillsThis skill inherits all available tools. When active, it can use any tool Claude has access to.
references/common-patterns.mdreferences/cron-expressions-reference.mdreferences/integration-patterns.mdreferences/testing-guide.mdreferences/wrangler-config.mdtemplates/basic-scheduled.tstemplates/hono-with-scheduled.tstemplates/multiple-crons.tstemplates/scheduled-with-bindings.tstemplates/wrangler-cron-config.jsoncStatus: Production Ready ✅ Last Updated: 2025-11-25 Dependencies: cloudflare-worker-base (for Worker setup) Latest Versions: wrangler@4.50.0, @cloudflare/workers-types@4.20251125.0
src/index.ts:
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:
scheduled (not scheduledHandler or onScheduled)wrangler.jsonc:
{
"name": "my-scheduled-worker",
"main": "src/index.ts",
"compatibility_date": "2025-10-23",
"triggers": {
"crons": [
"0 * * * *" // Every hour at minute 0
]
}
}
CRITICAL:
minute hour day-of-month month day-of-week# Enable scheduled testing
bunx 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/cdn-cgi/handler/scheduled insteadnpm run deploy
# or
bunx wrangler deploy
After deployment:
Load immediately when user mentions:
cron-expressions-reference.md → "cron syntax", "schedule format", "expression", "minute hour day", "every X minutes"common-patterns.md → "examples", "use cases", "patterns", "real-world", "database cleanup", "report generation", "how to"integration-patterns.md → "implement", "Hono", "multiple triggers", "bindings", "workflows", "error handling"wrangler-config.md → "configuration", "wrangler.jsonc", "multiple crons", "environment-specific", "dev staging production"testing-guide.md → "test", "local development", "__scheduled", "unit test", "curl", "debugging"Load proactively when:
integration-patterns.mdwrangler-config.mdcron-expressions-reference.mdtesting-guide.mdcommon-patterns.md* * * * *
│ │ │ │ │
│ │ │ │ └─── Day of Week (0-6, Sunday=0)
│ │ │ └───── Month (1-12)
│ │ └─────── Day of Month (1-31)
│ └───────── Hour (0-23)
└─────────── Minute (0-59)
| Character | Meaning | Example |
|---|---|---|
* | Every | * * * * * = every minute |
, | List | 0,30 * * * * = every hour at :00 and :30 |
- | Range | 0 9-17 * * * = every hour from 9am-5pm |
/ | Step | */15 * * * * = every 15 minutes |
# 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
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
}
controller.cron (string)The cron expression that triggered this execution.
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.
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.
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).
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:
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.
6 production-ready cron patterns:
controller.cron to route executionLoad references/integration-patterns.md for complete implementations with code examples, configuration details, and best practices.
Add cron triggers to wrangler.jsonc in the triggers.crons array. Each trigger requires a cron expression. Supports multiple crons (Free: 3 max, Paid: higher limits) and environment-specific configurations for dev/staging/production deployments.
Load references/wrangler-config.md for complete configuration examples including multiple triggers, environment-specific schedules, timezone handling, and removal procedures.
Test scheduled functions locally using the /__scheduled endpoint by running bunx wrangler dev --test-scheduled, then triggering handlers with curl "http://localhost:8787/__scheduled?cron=0+*+*+*+*" (use + instead of spaces in cron expressions).
Load references/testing-guide.md for complete testing strategies, local development setup, unit testing examples, integration testing patterns, and production monitoring techniques.
Run cron triggers only in data centers powered by renewable energy.
Via Dashboard:
Applies to:
How it works:
This skill prevents 6 documented issues:
Error: Cron triggers updated in wrangler.jsonc but not executing
Source: Cloudflare Docs - Cron Triggers
Why It Happens:
Prevention:
wrangler triggers deploy for trigger-only changes# If you only changed triggers (not code), use:
bunx wrangler triggers deploy
# Wait 15 minutes, then verify in dashboard
Error: Handler does not export a 'scheduled' method
Source: Common deployment error
Why It Happens:
scheduledPrevention:
// ❌ Wrong: Incorrect handler name
export default {
async scheduledHandler(controller, env, ctx) { }
};
// ❌ Wrong: Not in default export
export async function scheduled(controller, env, ctx) { }
// ✅ Correct: Named 'scheduled' in default export
export default {
async scheduled(controller, env, ctx) { }
};
Error: Cron runs at wrong time
Source: User expectation vs. reality
Why It Happens:
Prevention:
Convert your local time to UTC manually:
// Want to run at 9am PST (UTC-8)?
// 9am PST = 5pm UTC (17:00)
{
"triggers": {
"crons": ["0 17 * * *"] // 9am PST = 5pm UTC
}
}
// Want to run at 6pm EST (UTC-5)?
// 6pm EST = 11pm UTC (23:00)
{
"triggers": {
"crons": ["0 23 * * *"] // 6pm EST = 11pm UTC
}
}
// Remember: DST changes affect conversion!
// PST is UTC-8, PDT is UTC-7
Tools:
Error: Cron doesn't execute, no error shown
Source: Silent validation failure
Why It Happens:
Prevention:
# ❌ Wrong: Too many fields (6 fields instead of 5)
"crons": ["0 0 * * * *"] # Has seconds field - not supported
# ❌ Wrong: Invalid minute range
"crons": ["65 * * * *"] # Minute must be 0-59
# ❌ Wrong: Invalid day of week
"crons": ["0 0 * * 7"] # Day of week is 0-6 (use 0 for Sunday)
# ✅ Correct: 5 fields, valid ranges
"crons": ["0 0 * * 0"] # Sunday at midnight UTC
Validation:
--test-scheduledError: Worker must use ES modules format
Source: Legacy Service Worker format
Why It Happens:
Prevention:
// ❌ Wrong: Service Worker format
addEventListener('scheduled', (event) => {
event.waitUntil(handleScheduled(event));
});
// ✅ Correct: ES modules format
export default {
async scheduled(controller, env, ctx) {
await handleScheduled(controller, env, ctx);
},
};
Error: CPU time limit exceeded
Source: Long-running scheduled tasks
Why It Happens:
Prevention:
Option 1: Increase CPU limit in wrangler.jsonc
{
"limits": {
"cpu_ms": 300000 // 5 minutes (max for Standard plan)
}
}
Option 2: Use Workflows for long-running tasks
// Instead of long task in cron:
export default {
async scheduled(controller, env, ctx) {
// Trigger Workflow that can run for hours
await env.MY_WORKFLOW.create({
params: { task: 'long-running-job' },
});
},
};
Option 3: Break into smaller chunks
export default {
async scheduled(controller, env, ctx) {
// Process in batches
const batch = await getNextBatch(env.DB);
for (const item of batch) {
await processItem(item);
}
// If more work, send to Queue for next batch
const hasMore = await hasMoreWork(env.DB);
if (hasMore) {
await env.MY_QUEUE.send({ type: 'continue-processing' });
}
},
};
scheduled, not scheduledHandler or variantswrangler dev --test-scheduled--test-scheduled firstLoad references/common-patterns.md for 10 real-world cron patterns including database cleanup, API data collection, daily reports generation, cache warming, monitoring & health checks, data synchronization, backup automation, sitemap generation, webhook processing, and scheduled notifications.
// Scheduled event controller
interface ScheduledController {
readonly cron: string;
readonly type: string;
readonly scheduledTime: number;
}
// Execution context
interface ExecutionContext {
waitUntil(promise: Promise<any>): void;
passThroughOnException(): void;
}
// Scheduled handler
export default {
async scheduled(
controller: ScheduledController,
env: Env,
ctx: ExecutionContext
): Promise<void>;
}
| Feature | Free Plan | Paid Plan |
|---|---|---|
| Cron triggers per Worker | 3 | Higher (check docs) |
| CPU time per execution | 10 ms (avg) | 30 seconds (default), 5 min (max) |
| Wall clock time | 30 seconds | 15 minutes |
| Memory | 128 MB | 128 MB |
Cron triggers use Standard Workers pricing:
Cron execution = 1 request
Example:
Cost:
High frequency example:
Possible causes:
Solution:
# Re-deploy
bunx wrangler deploy
# Wait 15 minutes
# Check dashboard
# Workers & Pages > [Worker] > Cron Triggers
# Check logs
# Workers & Pages > [Worker] > Logs > Real-time Logs
Possible causes:
Solution:
export default {
async scheduled(controller, env, ctx) {
try {
await yourTask(env);
} catch (error) {
// Log detailed error
console.error('Handler failed:', {
error: error.message,
stack: error.stack,
cron: controller.cron,
time: new Date(controller.scheduledTime),
});
// Send alert
ctx.waitUntil(sendAlert(error));
// Re-throw to mark as failed
throw error;
}
},
};
Check logs in dashboard for error details.
Cause: UTC vs. local timezone confusion
Solution:
Convert your desired local time to UTC:
// Want 9am PST (UTC-8)?
// 9am PST = 5pm UTC (17:00)
{
"triggers": {
"crons": ["0 17 * * *"]
}
}
Tools:
Possible causes:
--test-scheduled flag/__scheduled)/cdn-cgi/handler/scheduled)Solution:
# Correct: Start with flag
bunx wrangler dev --test-scheduled
# In another terminal
curl "http://localhost:8787/__scheduled?cron=0+*+*+*+*"
Before deploying cron triggers to production:
scheduled in default exportlimits.cpu_ms)--test-scheduledLast Updated: 2025-10-23 Version: 1.0.0 Maintainer: Claude Skills Maintainers | maintainers@example.com
Expert guidance for Next.js Cache Components and Partial Prerendering (PPR). **PROACTIVE ACTIVATION**: Use this skill automatically when working in Next.js projects that have `cacheComponents: true` in their next.config.ts/next.config.js. When this config is detected, proactively apply Cache Components patterns and best practices to all React Server Component implementations. **DETECTION**: At the start of a session in a Next.js project, check for `cacheComponents: true` in next.config. If enabled, this skill's patterns should guide all component authoring, data fetching, and caching decisions. **USE CASES**: Implementing 'use cache' directive, configuring cache lifetimes with cacheLife(), tagging cached data with cacheTag(), invalidating caches with updateTag()/revalidateTag(), optimizing static vs dynamic content boundaries, debugging cache issues, and reviewing Cache Component implementations.
Creating algorithmic art using p5.js with seeded randomness and interactive parameter exploration. Use this when users request creating art using code, generative art, algorithmic art, flow fields, or particle systems. Create original algorithmic art rather than copying existing artists' work to avoid copyright violations.