AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Cloudflare Cron Triggers

skill-jackspace-claudeskillz-cloudflare-cron-triggers · by jackspace

|

No reviews yet
0 installs
36 views
0.0% view→install

Install

$ agentstack add skill-jackspace-claudeskillz-cloudflare-cron-triggers

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access Used
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets Used
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-jackspace-claudeskillz-cloudflare-cron-triggers)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
10mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Cloudflare Cron Triggers? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

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:

export default {
  async scheduled(
    controller: ScheduledController,
    env: Env,
    ctx: ExecutionContext
  ): Promise {
    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:

{
  "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

# 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

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

| 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 |

Common Patterns

# 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

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.

export default {
  async scheduled(controller: ScheduledController, env: Env): Promise {
    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 {
    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 { // Use ctx.waitUntil() for async operations that should complete ctx.waitUntil(logToAnalytics(env)); }, };


### `ctx.waitUntil(promise: Promise)`

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 {
    // 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)

// src/index.ts
interface Env {
  DB: D1Database;
  MY_BUCKET: R2Bucket;
}

export default {
  async scheduled(
    controller: ScheduledController,
    env: Env,
    ctx: ExecutionContext
  ): Promise {
    console.log('Running scheduled maintenance...');

    // Database cleanup
    await env.DB.prepare('DELETE FROM sessions WHERE expires_at ();

// 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 {
    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:

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

src/index.ts:

export default {
  async scheduled(
    controller: ScheduledController,
    env: Env,
    ctx: ExecutionContext
  ): Promise {
    // 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:

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 {
    // 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:

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

src/index.ts:

interface Env {
  DAILY_REPORT: Workflow;
}

export default {
  async scheduled(controller: ScheduledController, env: Env): Promise {
    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

export default {
  async scheduled(
    controller: ScheduledController,
    env: Env,
    ctx: ExecutionContext
  ): Promise {
    try {
      // Main task
      await performScheduledTask(env);
    } catch (error) {
      // Log error
      console.error('Scheduled task failed:', error);

      // Send alert
      await sendAlert({
        worker: 'my-scheduled-worker',
        cron: controller.cron,
        error: error.message,
        timestamp: new Date(controller.scheduledTime).toISOString(),
      });

      // Store failure in database
      ctx.waitUntil(
        env.DB.prepare(
          'INSERT INTO cron_failures (cron, error, timestamp) VALUES (?, ?, ?)'
        )
          .bind(controller.cron, error.message, Date.now())
          .run()
      );

      // Re-throw to mark execution as failed
      throw error;
    }
  },
};

async function sendAlert(details: any): Promise {
  await fetch('https://hooks.slack.com/services/YOUR/WEBHOOK/URL', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      text: `🚨 Cron job failed: ${details.worker}`,
      blocks: [
        {
          type: 'section',
          fields: [
            { type: 'mrkdwn', text: `*Worker:*\n${details.worker}` },
            { type: 'mrkdwn', text: `*Cron:*\n${details.cron}` },
            { type: 'mrkdwn', text: `*Error:*\n${details.error}` },
            { type: 'mrkdwn', text: `*Time:*\n${details.timestamp}` },
          ],
        },
      ],
    }),
  });
}

Wrangler Configuration

Basic Configuration

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

Multiple Cron Triggers

{
  "triggers": {
    "crons": [
      "*/5 * * * *",     // Every 5 minutes
      "0 */6 * * *",     // Every 6 hours
      "0 2 * * *",       // Daily at 2am UTC
      "0 0 * * 1"        // Weekly on Monday at midnight UTC
    ]
  }
}

Limits:

  • Free: 3 cron schedules max
  • Paid: Higher limits (check current limits)

Environment-Specific Crons

{
  "name": "my-worker",
  "main": "src/index.ts",
  "env": {
    "dev": {
      "triggers": {
        "crons": ["*/5 * * * *"]  // Dev: every 5 minutes for testing
      }
    },
    "staging": {
      "triggers": {
        "crons": ["*/30 * * * *"]  // Staging: every 30 minutes
      }
    },
    "production": {
      "triggers": {
        "crons": ["0 * * * *"]  // Production: hourly
      }
    }
  }
}

Deploy specific environment:

# Deploy to dev
npx wrangler deploy --env dev

# Deploy to production
npx wrangler deploy --env production

Removing All Cron Triggers

{
  "triggers": {
    "crons": []  // Empty array removes all crons
  }
}

After deploy, Worker will no longer execute on schedule.


Testing & Development

Local Testing with Wrangler

# Start dev server with scheduled testing enabled
npx wrangler dev --test-scheduled

This exposes /__scheduled endpoint for triggering scheduled handlers.


Trigger Scheduled Handler

# Trigger with default cron (if only one configured)
curl "http://localhost:8787/__scheduled"

# Trigger with specific cron expression
curl "http://localhost:8787/__scheduled?cron=0+*+*+*+*"

# Trigger with URL-encoded cron
curl "http://localhost:8787/__scheduled?cron=*/5+*+*+*+*"

Note: Use + instead of spaces in URL, or URL-encode properly.


Verify Handler Output

# Start dev server
npx wrangler dev --test-scheduled

# In another terminal, trigger and watch output
curl "http://localhost:8787/__scheduled?cron=0+*+*+*+*"

Output appears in wrangler dev terminal:

[wrangler:inf] GET /__scheduled?cron=0+*+*+*+* 200 OK (45ms)
Cron job executed at: 2025-10-23T15:00:00.000Z
Triggered by cron: 0 * * * *
Scheduled task completed successfully

Test Multiple Cron Expressions

# Test hourly cron
curl "http://localhost:8787/__scheduled?cron=0+*+*+*+*"

# Test daily cron
curl "http://localhost:8787/__scheduled?cron=0+0+*+*+*"

# Test weekly cron
curl "http://localhost:8787/__scheduled?cron=0+0+*+*+1"

Python Workers Testing

# Python Workers use different endpoint
curl "http://localhost:8787/cdn-cgi/handler/scheduled?cron=*+*+*+*+*"

Green Compute

Run cron triggers only in data centers powered by renewable energy.

Enable Green Compute

Via Dashboard:

  1. Go to Workers & Pages
  2. In Account details section, find Compute Setting
  3. Click Change
  4. Select Green Compute
  5. Click Confirm

Applies to:

  • All cron triggers in your account
  • Reduces carbon footprint
  • No additional cost
  • May introduce slight delays in some regions

How it works:

  • Cloudflare routes cron executions to green-powered data centers
  • Uses renewable energy: wind, solar, hydroelectric
  • Verified through P

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.