How to Use Cron Jobs for Scheduled Tasks in Deno: A Complete API Guide

Deno provides a built-in unstable Deno.cron() API that registers functions to run on recurring schedules using Unix-style cron strings or JSON schedules, requiring the --unstable-cron flag to enable.

The denoland/deno runtime includes native cron functionality that eliminates the need for external scheduling libraries. This unstable API, implemented in the runtime extension at ext/cron/01_cron.ts, allows developers to define scheduled tasks directly within their Deno applications with built-in support for cancellation signals, automatic retries, and OpenTelemetry instrumentation.

Understanding the Deno.cron() API

Deno's cron implementation is an unstable feature that must be explicitly enabled using the --unstable-cron command-line flag. The API is declared in [cli/tsc/dts/lib.deno.unstable.d.ts](https://github.com/denoland/deno/blob/main/cli/tsc/dts/lib.deno.unstable.d.ts) and implemented in [ext/cron/01_cron.ts](https://github.com/denoland/deno/blob/main/ext/cron/01_cron.ts).

The Deno.cron() function registers a named handler function that executes according to a specified schedule. Unlike external cron services, Deno's implementation runs within the runtime process, making it ideal for background tasks, periodic maintenance, and automated reporting within Deno applications.

How Deno Cron Jobs Work Under the Hood

The implementation follows a structured lifecycle from validation to execution:

1. Argument Validation

When Deno.cron() is invoked, the wrapper validates the name, schedule, and handler arguments immediately. The name must be unique for the process lifetime and can only contain alphanumeric characters, hyphens, and underscores (a-zA-Z0-9_-). Invalid schedules trigger early errors at lines 116-143 of the implementation.

2. Resource Creation

After validation, the function calls the low-level op op_cron_create imported from ext:core/ops. This registers the job with Deno's Rust runtime, which stores the parsed schedule and returns a resource ID (RID).

3. Execution and Backoff

The Rust side schedules timers based on the cron expression. Each tick triggers op_cron_next, resolving a promise that the JavaScript side awaits. When handlers throw errors, Deno implements exponential backoff using the optional backoffSchedule parameter, retrying execution according to specified millisecond delays.

4. Telemetry Integration

Each cron job automatically creates an OpenTelemetry span named deno.cron with attributes including deno.cron.name and deno.cron.schedule (see lines 191-192). Errors inside handlers are logged using import.meta.log("error", …).

5. Cancellation Support

Passing an AbortSignal (usually from an AbortController) allows programmatic cancellation. When the signal aborts, the implementation closes the cron resource around line 132, preventing further executions and cleaning up resources.

API Signature and Options

The Deno.cron() signature accepts four parameters with one optional overload for configuration:

Deno.cron(
  name: string,
  schedule: string | CronSchedule,
  options?: {
    signal?: AbortSignal;
    backoffSchedule?: number[]; // milliseconds between retries
  },
  handler: () => void | Promise<void>
): Promise<void>;

Parameter details:

  • name: Unique identifier for the cron job (required, process-unique)
  • schedule: Either a cron string ("*/5 * * * *") or JSON schedule ({ minute: { every: 5 } })
  • options.signal: AbortSignal for programmatic cancellation
  • options.backoffSchedule: Array of retry delays in milliseconds when the handler throws
  • handler: Synchronous or asynchronous function to execute on schedule

Practical Implementation Examples

Basic Cron Job with Unix String

Create a simple job that executes every minute using classic cron syntax:

// Run with: deno run --allow-all --unstable-cron basic.ts
Deno.cron(
  "log-timestamp",
  "*/1 * * * *",               // every minute
  () => console.log("Current time:", new Date()),
);

This registers a job named log-timestamp that outputs the current timestamp each minute.

JSON Schedule Format

Use the JSON schedule API for programmatic schedule construction:

Deno.cron(
  "hourly-cleanup",
  { hour: { every: 1 } },      // every hour at minute 0
  () => console.log("Running hourly cleanup task"),
);

The JSON format supports minute, hour, dayOfMonth, month, and dayOfWeek fields, matching Unix cron components.

Programmatic Cancellation with AbortSignal

Stop cron jobs dynamically using an AbortController:

const controller = new AbortController();

Deno.cron(
  "temporary-task",
  "*/2 * * * *",               // every 2 minutes
  { signal: controller.signal },
  () => console.log("Processing…"),
);

// Cancel after 5 minutes
setTimeout(() => controller.abort(), 5 * 60_000);

When controller.abort() executes, Deno closes the cron resource and halts further invocations.

Automatic Retry with Backoff

Implement resilient scheduled tasks using the backoff feature:

let attempts = 0;
Deno.cron(
  "unreliable-task",
  "*/1 * * * *",
  { backoffSchedule: [1_000, 3_000, 10_000] }, // 1s, 3s, 10s retries
  () => {
    attempts++;
    console.log(`Attempt ${attempts}`);
    if (attempts < 3) throw new Error("Temporary failure");
    console.log("Success on attempt", attempts);
  },
);

If the handler throws, Deno pauses for the specified duration before retrying, up to the array length limit.

Production-Ready Implementation

Combine all features for robust production scheduling:

const controller = new AbortController();

Deno.cron(
  "api-health-check",
  { minute: { every: 5 }, hour: { every: 1 } }, // every 5 minutes
  { 
    signal: controller.signal, 
    backoffSchedule: [2_000, 5_000] 
  },
  async () => {
    try {
      const resp = await fetch("https://api.example.com/health");
      if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
      console.log("Health check passed");
    } catch (e) {
      console.error("Health check failed:", e);
      throw e; // trigger backoff retry
    }
  },
);

// Graceful shutdown after 30 minutes
setTimeout(() => controller.abort(), 30 * 60_000);

Key Source Files and Testing

The cron functionality spans multiple files in the Deno repository:

According to the Deno source code, the implementation relies on Rust-side timer management for schedule parsing while delegating handler execution to the JavaScript event loop.

Summary

  • Enable the unstable API using the --unstable-cron flag when running Deno.
  • Use Deno.cron() in ext/cron/01_cron.ts to register named, scheduled handlers with optional cancellation signals.
  • Specify schedules using either Unix cron strings ("*/5 * * * *") or JSON objects ({ minute: { every: 5 } }).
  • Implement resilient tasks using backoffSchedule for automatic retries with specified millisecond delays.
  • Cancel jobs programmatically by passing an AbortSignal from an AbortController.
  • Each cron job creates OpenTelemetry spans automatically for observability in production environments.

Frequently Asked Questions

Is Deno.cron() stable for production use?

No, Deno.cron() is currently an unstable API as of the latest Deno releases. You must launch Deno with the --unstable-cron flag to access the functionality. While the API is tested extensively in tests/unit/cron_test.ts, unstable features may undergo breaking changes in future releases.

How do I stop a running cron job in Deno?

Pass an AbortSignal via the options parameter. Create an AbortController, pass controller.signal to Deno.cron(), and call controller.abort() when you need to stop the job. According to the implementation in ext/cron/01_cron.ts around line 132, this closes the underlying cron resource and prevents further executions.

What happens if my cron handler throws an error?

If the handler function throws, Deno checks for a backoffSchedule option. If provided, the runtime waits for the specified milliseconds (first array value for first failure, second for second failure, etc.) and retries execution. If no backoff schedule exists or retries are exhausted, the error is logged via OpenTelemetry and the next scheduled execution proceeds normally.

Can I use both cron strings and JSON for scheduling?

Yes. The schedule parameter accepts either a string in Unix cron format ("0 9 * * 1") or a JSON object with fields like minute, hour, dayOfMonth, month, and dayOfWeek. The JSON format is useful when constructing schedules programmatically or when specific time components require dynamic configuration.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →