---
title: Scheduling & Cron
description: How recurring scheduling works, including supported 5-field and 6-field cron formats.
order: 22
---

# Scheduling & Cron

DurableStack recurring scheduling is cron-driven.

Each recurring job stores its schedule definition, and the scheduler materializes due runs into the run queue.

## How scheduling works

At runtime:

- Recurring job definitions are initialized/upserted at startup.
- Scheduler checks for due recurring jobs.
- For each due job, DurableStack enqueues a run and computes the next occurrence.
- Catch-up behavior is controlled by `options.Recurring.CatchUpPolicy`.

The same job can still be manually enqueued through `IDurableStackClient`.

## Supported cron formats

DurableStack supports both:

- 5-field cron: `minute hour day-of-month month day-of-week`
- 6-field cron: `second minute hour day-of-month month day-of-week`

Examples:

- 5-field: `*/5 * * * *`
- 6-field: `*/20 * * * * *`

Any other field count is invalid.

## Common examples

- Every minute: `* * * * *`
- Every 5 minutes: `*/5 * * * *`
- Every hour at minute 0: `0 * * * *`
- Every day at 02:00: `0 2 * * *`
- Weekdays at 08:30: `30 8 * * 1-5`
- First day of month at midnight: `0 0 1 * *`
- Every 20 seconds (6-field): `*/20 * * * * *`

## Attribute examples

```csharp
using DurableStack.Core.Abstractions;
using DurableStack.Hosting.DependencyInjection;

[DurableJob(Name = "report-hourly")]
[RecurringJob("0 * * * *", TimeZone = "UTC")]
public sealed class HourlyReportJob : IDurableJob
{
    public Task ExecuteAsync(JobContext context, CancellationToken cancellationToken = default)
        => Task.CompletedTask;
}

[DurableJob(Name = "heartbeat-every-20-seconds")]
[RecurringJob("*/20 * * * * *", TimeZone = "UTC")]
public sealed class SubMinuteHeartbeatJob : IDurableJob
{
    public Task ExecuteAsync(JobContext context, CancellationToken cancellationToken = default)
        => Task.CompletedTask;
}
```

## Catch-up policy interaction

- `SkipMissed` (default): jumps to the next future slot after downtime.
- `CatchUp`: materializes missed slots incrementally until current time.

Use `SkipMissed` for most production workloads unless strict missed-slot replay is required.

## Time zones

Cron expressions are evaluated in the `RecurringJob(TimeZone = "...")` IANA zone.

- Default zone is `UTC`.
- Windows time zone IDs are not supported.

For more detail, see [Time Zones](time-zones).

## Practical guidance

- Prefer 5-field cron for normal business schedules.
- Use 6-field cron only when sub-minute cadence is truly required.
- Keep `AllowConcurrentRuns = false` unless overlap is intentional.
- Validate cron expressions in tests for critical schedules.
