Durable Object alarms are per-object timers; crons are global

ctx.storage.setAlarm(when) schedules one wake-up per object; alarm() runs with the object's storage and retries on failure. Call setAlarm again to recur.

Durable Objects

· Chapter

57

·

2

min read

The answer. A Cron Trigger is one schedule for the whole Worker. When the schedule belongs to a thing (expire this session in 30 minutes, retry this tenant's sync at 09:00 in its timezone, flush this buffer 5 s after the last write), that thing should be a Durable Object with an alarm. this.ctx.storage.setAlarm(timestamp) stores one pending wake-up per object (setting it again replaces it); when it fires, the runtime calls alarm() on that object with its storage intact, even if the object had been evicted. If alarm() throws, it is retried with backoff; if it returns, the alarm is consumed, so recurring work ends by calling setAlarm again. Debouncing falls out for free: every write moves the alarm forward, and the flush runs once, after the last one.

The pattern.

export class Buffer extends DurableObject<Env> {
  async append(event: unknown) {
    this.ctx.storage.sql.exec("insert into pending (body) values (?)", JSON.stringify(event));
    await this.ctx.storage.setAlarm(Date.now() + 5_000);          // slide the flush 5 s after the latest write
  }
  async alarm() {
    const rows = this.ctx.storage.sql.exec("select id, body from pending").toArray();
    if (rows.length === 0) return;
    await this.env.SINK.sendBatch(rows.map((r) => ({ body: JSON.parse(r.body as string) })));
    this.ctx.storage.sql.exec("delete from pending where id <= ?", rows.at(-1)!.id);
  }
}

Watch out.

  • An alarm handler has 15 minutes of wall time and runs concurrently with other requests to the object; do not assume it holds a lock.
  • deleteAll() now deletes the pending alarm too (compatibility date 2026-02-24 or later). Older Workers need a separate deleteAlarm().
  • Each setAlarm is billed as a row written. A sliding alarm on a hot object is cheap; a setAlarm per event on millions of objects is a line item.

Related: durable-object-one-instance-per-id-with-sqlite · cron-triggers-scheduled-handler-basics · queues-batches-acks-and-retries