Scheduled Tasks

Scheduled tasks run your worker at specific times or intervals using cron expressions. Use them for background jobs like sending emails, cleaning up data, syncing external APIs, or generating reports.

Cron Syntax

OpenWorkers supports standard cron expressions with an optional seconds field.

5-field format (standard)

┌───────────── minute (0-59)
│ ┌───────────── hour (0-23)
│ │ ┌───────────── day of month (1-31)
│ │ │ ┌───────────── month (1-12)
│ │ │ │ ┌───────────── day of week (0-6, Sun=0)
│ │ │ │ │
* * * * *

6-field format (with seconds)

┌───────────── second (0-59)
│ ┌───────────── minute (0-59)
│ │ ┌───────────── hour (0-23)
│ │ │ ┌───────────── day of month (1-31)
│ │ │ │ ┌───────────── month (1-12)
│ │ │ │ │ ┌───────────── day of week (0-6, Sun=0)
│ │ │ │ │ │
* * * * * *

Examples

ExpressionDescription
* * * * *Every minute
*/5 * * * *Every 5 minutes
0 * * * *Every hour
0 0 * * *Every day at midnight
0 9 * * 1Every Monday at 9:00 AM
0 0 1 * *First day of every month
*/30 * * * * *Every 30 seconds (6-field)
0 */5 * * * *Every 5 minutes at second 0 (6-field)

Special characters

CharacterMeaningExample
*Any value* * * * * (every minute)
*/nEvery n units*/15 * * * * (every 15 min)
n-mRange0-30 * * * * (minutes 0-30)
n,mList0,30 * * * * (at 0 and 30)

Scheduled Event

When your cron triggers, the worker receives a scheduled event with the following properties:

PropertyDescription
scheduledTimeWhen the task was scheduled to run (Unix timestamp in milliseconds)

The modern syntax. Your handler receives event, env, and ctx as arguments.

export default {
  async scheduled(event: ScheduledEvent, env: Env, ctx: ExecutionContext): Promise<void> {
    console.log(`Running at ${new Date(event.scheduledTime).toISOString()}`);

    // Access bindings via env
    await env.DB.query("INSERT INTO logs (time) VALUES ($1)", [event.scheduledTime]);
  }
}

Parameters

ParameterTypeDescription
eventScheduledEventEvent object with scheduledTime
envEnvObject containing environment variables and bindings
ctxExecutionContextExecution context with waitUntil()

Service Worker (legacy)

The older syntax using addEventListener. Still supported for backwards compatibility.

addEventListener('scheduled', (event: ScheduledEvent) => {
  event.waitUntil(handleSchedule(event.scheduledTime));
});

async function handleSchedule(scheduledTime: number): Promise<void> {
  console.log(`Running at ${new Date(scheduledTime).toISOString()}`);

  // Access bindings via globalThis.env
  await globalThis.env.DB.query("INSERT INTO logs (time) VALUES ($1)", [scheduledTime]);
}

ScheduledEvent interface

interface ScheduledEvent {
  scheduledTime: number;
  waitUntil(promise: Promise<any>): void;
}

Practical Examples

Daily cleanup

Delete old records every day at 3:00 AM.

0 3 * * *
export default {
  async scheduled(event, env, ctx) {
    const cutoff = Date.now() - 30 * 24 * 60 * 60 * 1000; // 30 days ago

    await fetch('https://api.example.com/records/cleanup', {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${env.API_TOKEN}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ before: cutoff })
    });

    console.log('Cleanup completed');
  }
}

Hourly sync

Sync data from external API every hour.

0 * * * *
export default {
  async scheduled(event, env, ctx) {
    const response = await fetch('https://external-api.com/data');
    const data = await response.json();

    await fetch('https://api.example.com/import', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(data)
    });

    console.log(`Synced ${data.length} records`);
  }
}

Health check every 5 minutes

Ping your services and alert on failure.

*/5 * * * *
export default {
  async scheduled(event, env, ctx) {
    const services = [
      'https://api.example.com/health',
      'https://app.example.com/health'
    ];

    for (const url of services) {
      try {
        const response = await fetch(url, { method: 'HEAD' });

        if (!response.ok) {
          await sendAlert(env, `${url} returned ${response.status}`);
        }
      } catch (error) {
        await sendAlert(env, `${url} is unreachable: ${error.message}`);
      }
    }
  }
}

async function sendAlert(env, message) {
  await fetch(env.SLACK_WEBHOOK, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ text: `🚨 ${message}` })
  });
}

Setting Up Cron Triggers

  1. Go to your worker in the dashboard
  2. Click on Cron Triggers
  3. Add a cron expression
  4. Save

Cron configuration


Limits

Scheduled tasks have the same limits as HTTP requests:

ResourceLimit
CPU Time100ms
Wall Clock60 seconds
Memory128 MB

See Limits & Quotas for details.


Tips

  • Use ctx.waitUntil() to ensure async operations complete before the worker terminates
  • Log important milestones for debugging
  • Set appropriate timeouts on external API calls
  • Use secrets for API keys and tokens
  • Keep tasks idempotent (safe to retry)