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

> Master Deno cron jobs with the unstable Deno.cron API Schedule tasks using cron strings or JSON Learn how to automate recurring operations in your Deno applications.

- Repository: [Deno/deno](https://github.com/denoland/deno)
- Tags: api-reference
- Published: 2026-02-26

---

**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`](https://github.com/denoland/deno/blob/main/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)](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)](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:

```typescript
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:

```typescript
// 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:

```typescript
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`:

```typescript
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:

```typescript
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:

```typescript
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:

- **[[`ext/cron/01_cron.ts`](https://github.com/denoland/deno/blob/main/ext/cron/01_cron.ts)](https://github.com/denoland/deno/blob/main/ext/cron/01_cron.ts)**: Runtime implementation handling validation, resource management, and telemetry
- **[[`cli/tsc/dts/lib.deno.unstable.d.ts`](https://github.com/denoland/deno/blob/main/cli/tsc/dts/lib.deno.unstable.d.ts)](https://github.com/denoland/deno/blob/main/cli/tsc/dts/lib.deno.unstable.d.ts)**: TypeScript type definitions and function signatures
- **[[`tests/unit/cron_test.ts`](https://github.com/denoland/deno/blob/main/tests/unit/cron_test.ts)](https://github.com/denoland/deno/blob/main/tests/unit/cron_test.ts)**: Unit tests verifying validation, scheduling logic, backoff behavior, and signal handling
- **[[`tests/specs/run/unstable/cron.ts`](https://github.com/denoland/deno/blob/main/tests/specs/run/unstable/cron.ts)](https://github.com/denoland/deno/blob/main/tests/specs/run/unstable/cron.ts)**: Integration tests demonstrating real cron execution in the test harness

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`](https://github.com/denoland/deno/blob/main/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`](https://github.com/denoland/deno/blob/main/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`](https://github.com/denoland/deno/blob/main/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.