Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions src/content/changelog/workflows/2026-05-13-cron-workflows.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
title: Schedule Workflow instances directly from your Workflow binding
description: Add cron schedules to a Workflow binding in wrangler.jsonc to create Workflow instances automatically on a recurring interval.
products:
- workflows
- workers
date: 2026-05-13
---

You can now attach cron schedules directly to a Workflow binding in `wrangler.jsonc`. Each scheduled run creates a new Workflow instance automatically, so you do not need to define a separate Worker with a `scheduled` handler just to trigger your Workflow on an interval.

For example, you can configure hourly, every-15-minute, or weekday schedules on the same Workflow:

```jsonc
{
"workflows": [
{
"name": "my-scheduled-workflow",
"binding": "MY_WORKFLOW",
"class_name": "MyScheduledWorkflow",
"schedules": [
{ "cron": "0 * * * *" },
{ "cron": "*/15 * * * *" },
{ "cron": "0 9 * * MON-FRI" },
],
},
],
}
```

This makes it easier to build recurring jobs such as database backups, invoice generation, report aggregation, and cleanup tasks without wiring up a separate Cron Trigger entrypoint.

For more information, refer to [Trigger Workflows](/workflows/build/trigger-workflows/).
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,18 @@ sidebar:
order: 3
tableOfContents: false
description: |
Cloudflare Workflows provide a powerful way to manage asynchronous, durable processes. The ability to explicitly schedule tasks using cron triggers and pause execution with `step.sleep` allows developers to build sophisticated, time-aware applications.
Cloudflare Workflows provide a powerful way to manage asynchronous, durable processes. The ability to explicitly schedule tasks using scheduled handlers and pause execution with `step.sleep` allows developers to build sophisticated, time-aware applications.
products:
- workflows
- workers
---

import { Render, Tabs, TabItem, Card, YouTube } from "~/components";
import { Render, Tabs, TabItem, Card, WranglerConfig, YouTube } from "~/components";

<Tabs>
<TabItem label="Watch this episode">

Cloudflare Workflows provide a powerful way to manage asynchronous, durable processes. The ability to explicitly schedule tasks using cron triggers and pause execution with `step.sleep` allows developers to build sophisticated, time-aware applications.
Cloudflare Workflows provide a powerful way to manage asynchronous, durable processes. The ability to explicitly schedule tasks using scheduled handlers and pause execution with `step.sleep` allows developers to build sophisticated, time-aware applications.

<Card>
<YouTube id="L6gR4Yr3UW8" />
Expand Down Expand Up @@ -59,18 +59,32 @@ Cloudflare Workflows provide a powerful way to manage asynchronous, durable proc

### Wrangler Configuration

The Wrangler configuration file is used to configure your Worker and Workflows. This includes defining bindings to resources like KV namespaces and setting up triggers for workflows.
The Wrangler configuration file is used to configure your Worker and Workflows. This includes defining bindings to resources like KV namespaces and setting up schedules for workflows.

Open the Wrangler configuration file and find the `[triggers]` section.
[See here](https://github.com/craigsdennis/punderful-workflows/blob/main/wrangler.toml#L68)
The episode repository uses the older pattern of a top-level `[triggers]` section plus a `scheduled` handler in the main Worker to create a `LEADERBOARD_WORKFLOW` instance on a timer.

The `crons` array allows you to define scheduled triggers for your main Worker. The example shows a cron job configured to run every 30 minutes.
[See here](https://github.com/craigsdennis/punderful-workflows/blob/main/wrangler.toml#L68)

Locate the `scheduled` handler in your main Worker code (`src/index.tsx`). This handler is executed when a cron trigger fires.
In current Workflows projects, you can usually schedule the Workflow directly on its binding instead:

<WranglerConfig>

```jsonc
{
"workflows": [
{
"name": "leaderboard-workflow",
"binding": "LEADERBOARD_WORKFLOW",
"class_name": "LeaderboardWorkflow",
"schedules": [{ "cron": "*/30 * * * *" }]
}
]
}
```

[See here](https://github.com/craigsdennis/punderful-workflows/blob/main/src/index.tsx#L315)
</WranglerConfig>

This handler creates an instance of the `LEADERBOARD_WORKFLOW`, initiating the leaderboard update process on a schedule.
Use a separate Cron Trigger and `scheduled` handler only when you need custom logic before deciding whether to create a Workflow instance. Use the latest Wrangler release when configuring Workflow schedules.

### Puntificator: Using AI to Develop More Puns Automatically

Expand Down
2 changes: 1 addition & 1 deletion src/content/docs/workers/configuration/cron-triggers.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Cron Triggers are ideal for running periodic jobs, such as for maintenance or ca

:::note

Cron Triggers can also be combined with [Workflows](/workflows/) to trigger multi-step, long-running tasks. You can [bind to a Workflow](/workflows/build/workers-api/) directly from your Cron Trigger to execute a Workflow on a schedule.
Cron Triggers are also available directly in [Workflows](/workflows/) via `schedules` on the Workflow binding instead. Refer to [Trigger Workflows](/workflows/build/trigger-workflows/) for details, and use a Wrangler release that supports Workflow schedules.

:::

Expand Down
6 changes: 6 additions & 0 deletions src/content/docs/workers/wrangler/configuration.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -1194,6 +1194,11 @@ To bind Workflows to your Worker, assign an array of the below object to the `wo
- `script_name` <Type text="string" /> <MetaInfo text="optional" />
- The name of the Worker script where the Workflow class is defined. Only required if the Workflow is defined in a different Worker than the one the binding is configured on.

- `schedules` <Type text="{ cron: string }[]" /> <MetaInfo text="optional" />
- A list of cron schedules that create new instances of this Workflow automatically.
- Use this when you want to run a Workflow on a recurring interval without defining top-level `triggers.crons` and a separate `scheduled` handler.
- Use a Wrangler release that supports Workflow schedules. If your local schema does not recognize `schedules`, update Wrangler first.

Example:

<WranglerConfig>
Expand All @@ -1205,6 +1210,7 @@ Example:
"binding": "<BINDING_NAME>",
"name": "<WORKFLOW_NAME>",
"class_name": "<CLASS_NAME>",
"schedules": [{ "cron": "0 * * * *" }],
},
],
}
Expand Down
39 changes: 36 additions & 3 deletions src/content/docs/workflows/build/trigger-workflows.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,19 @@ import { TypeScriptExample, WranglerConfig } from "~/components";
You can trigger Workflows both programmatically and via the Workflows APIs, including:

1. With [Workers](/workers) via HTTP requests in a `fetch` handler, or bindings from a `queue` or `scheduled` handler
2. Using the [Workflows REST API](/api/resources/workflows/methods/list/)
3. Via the [wrangler CLI](/workers/wrangler/commands/workflows/#workflows) in your terminal
2. On a recurring interval by defining `schedules` on a Workflow binding in your Wrangler configuration
3. Using the [Workflows REST API](/api/resources/workflows/methods/list/)
4. Via the [wrangler CLI](/workers/wrangler/commands/workflows/#workflows) in your terminal

## Workers API (Bindings)

You can interact with Workflows programmatically from any Worker script by creating a binding to a Workflow. A Worker can bind to multiple Workflows, including Workflows defined in other Workers projects (scripts) within your account.

You can interact with a Workflow:
You can trigger a Workflow:

- Directly over HTTP via the [`fetch`](/workers/runtime-apis/handlers/fetch/) handler
- From a [Queue consumer](/queues/configuration/javascript-apis/#consumer) inside a `queue` handler
- On a recurring schedule by defining `schedules` on the Workflow binding in `wrangler.jsonc`
- From a [Cron Trigger](/workers/configuration/cron-triggers/) inside a `scheduled` handler
- Within a [Durable Object](/durable-objects/)

Expand Down Expand Up @@ -64,6 +66,37 @@ To bind to a Workflow from your Workers code, you need to define a [binding](/wo

The `binding = "MY_WORKFLOW"` line defines the JavaScript variable that our Workflow methods are accessible on, including `create` (which triggers a new instance) or `get` (which returns the status of an existing instance).

### Schedule a Workflow directly

If you want to create Workflow instances on a recurring interval, add a `schedules` array to the Workflow binding in your Wrangler configuration:

<WranglerConfig>

```jsonc
{
"$schema": "./node_modules/wrangler/config-schema.json",
"name": "workflows-tutorial",
"main": "src/index.ts",
"compatibility_date": "$today",
"workflows": [
{
"name": "workflows-tutorial",
"binding": "MY_WORKFLOW",
"class_name": "MyWorkflow",
"schedules": [{ "cron": "0 * * * *" }]
}
]
}
```

</WranglerConfig>

Each matching cron expression creates a new Workflow instance automatically. Use this when you want to run a Workflow on a schedule without defining top-level `triggers.crons` and a separate `scheduled` handler.

On [Workers Paid](/workers/platform/pricing/#workers), Workflow instances created by `schedules` can run for up to one hour per cron firing without consuming a Workflow concurrency slot. If the instance pauses or sleeps after that window, the instance yields and enters the normal concurrency queue upon resume. It resumes when a concurrency slot is available.

Use the latest Wrangler release when configuring Workflow schedules. If your local Wrangler schema does not recognize `schedules` yet, update Wrangler before deploying.

The following example shows how you can manage Workflows from within a Worker, including:

- Retrieving the status of an existing Workflow instance by its ID
Expand Down
51 changes: 21 additions & 30 deletions src/content/docs/workflows/examples/backup-d1.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ description: Send invoice when shopping cart is checked out and paid for
reviewed: 2024-12-30
---

import { TabItem, Tabs, WranglerConfig } from "~/components";
import { WranglerConfig } from "~/components";

In this example, we implement a Workflow periodically triggered by a [Cron Trigger](/workers/configuration/cron-triggers). That Workflow initiates a backup for a D1 database using the REST API, and then stores the SQL dump in an [R2](/r2) bucket.
In this example, we implement a Workflow that runs on a schedule using the `schedules` field on the Workflow binding. That Workflow initiates a backup for a D1 database using the REST API, and then stores the SQL dump in an [R2](/r2) bucket.

When the Workflow is triggered, it fetches the REST API to initiate an export job for a specific database. Then it fetches the same endpoint to check if the backup job is ready and the SQL dump is available to download.

Expand All @@ -40,18 +40,15 @@ type Env = {
BACKUP_WORKFLOW: Workflow;
D1_REST_API_TOKEN: string;
BACKUP_BUCKET: R2Bucket;
};

// Workflow parameters: we expect accountId and databaseId
type Params = {
accountId: string;
databaseId: string;
ACCOUNT_ID: string;
DATABASE_ID: string;
};

// Workflow logic
export class backupWorkflow extends WorkflowEntrypoint<Env, Params> {
async run(event: WorkflowEvent<Params>, step: WorkflowStep) {
const { accountId, databaseId } = event.payload;
export class backupWorkflow extends WorkflowEntrypoint<Env> {
async run(_event: WorkflowEvent<unknown>, step: WorkflowStep) {
const accountId = this.env.ACCOUNT_ID;
const databaseId = this.env.DATABASE_ID;

const url = `https://api.cloudflare.com/client/v4/accounts/${accountId}/d1/database/${databaseId}/export`;
const method = "POST";
Expand Down Expand Up @@ -105,18 +102,6 @@ export default {
async fetch(req: Request, env: Env): Promise<Response> {
return new Response("Not found", { status: 404 });
},
async scheduled(
controller: ScheduledController,
env: Env,
ctx: ExecutionContext,
) {
const params: Params = {
accountId: "{accountId}",
databaseId: "{databaseId}",
};
const instance = await env.BACKUP_WORKFLOW.create({ params });
console.log(`Started workflow: ${instance.id}`);
},
};
```

Expand All @@ -130,6 +115,8 @@ Here is a minimal package.json:
}
```

Create `D1_REST_API_TOKEN` as a [secret](/workers/configuration/secrets/) with permission to export the target D1 database.

Here is a [Wrangler configuration file](/workers/wrangler/configuration/):

<WranglerConfig>
Expand All @@ -143,25 +130,29 @@ Here is a [Wrangler configuration file](/workers/wrangler/configuration/):
"compatibility_flags": [
"nodejs_compat"
],
"vars": {
"ACCOUNT_ID": "account-id",
"DATABASE_ID": "database-id"
},
"workflows": [
{
"name": "backup-workflow",
"binding": "BACKUP_WORKFLOW",
"class_name": "backupWorkflow"
"class_name": "backupWorkflow",
"schedules": [{ "cron": "0 0 * * *" }]
}
],
"r2_buckets": [
{
"binding": "BACKUP_BUCKET",
"bucket_name": "d1-backups"
}
],
"triggers": {
"crons": [
"0 0 * * *"
]
}
]
}
```

</WranglerConfig>

Each scheduled run creates a new Workflow instance automatically.

Use the latest Wrangler release when configuring Workflow schedules. If your local Wrangler schema does not recognize `schedules` yet, update Wrangler before deploying.
33 changes: 30 additions & 3 deletions src/content/docs/workflows/get-started/guide.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -168,11 +168,38 @@ Follow the steps below to learn how to build a Workflow from scratch.
}
```

</WranglerConfig>
</WranglerConfig>

The `class_name` must match your exported class, and `binding` is the variable name you use to access the Workflow in your code (like `env.MY_WORKFLOW`).
The `class_name` must match your exported class, and `binding` is the variable name you use to access the Workflow in your code (like `env.MY_WORKFLOW`).

You can also access [bindings](/workers/runtime-apis/bindings/) (such as [KV](/kv/), [R2](/r2/), or [D1](/d1/)) via `this.env` within your Workflow. For more information on bindings within Workers, refer to [Bindings (env)](/workers/runtime-apis/bindings/).
If you want the same Workflow to run automatically on a recurring interval, add `schedules` to the Workflow definition:

<WranglerConfig>

```jsonc
{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "my-workflow",
"main": "src/index.ts",
"compatibility_date": "$today",
"workflows": [
{
"name": "my-workflow",
"binding": "MY_WORKFLOW",
"class_name": "MyWorkflow",
"schedules": [{ "cron": "0 * * * *" }]
}
]
}
```

</WranglerConfig>

Each matching cron expression creates a new Workflow instance automatically, so you do not need top-level `triggers.crons` and a separate `scheduled` handler for Workflow-specific recurring runs.

Use the latest Wrangler release when configuring Workflow schedules. If your local Wrangler schema does not recognize `schedules` yet, update Wrangler before deploying.

You can also access [bindings](/workers/runtime-apis/bindings/) (such as [KV](/kv/), [R2](/r2/), or [D1](/d1/)) via `this.env` within your Workflow. For more information on bindings within Workers, refer to [Bindings (env)](/workers/runtime-apis/bindings/).

2. Now, generate types for your bindings:

Expand Down
8 changes: 7 additions & 1 deletion src/content/docs/workflows/reference/limits.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ Workflows cannot be deployed to Workers for Platforms namespaces, as Workflows d

[^6]: Workflows will return a HTTP 429 rate limited error if you exceed the rate of new Workflow instance creation.

[^7]: Only instances with a `running` state count towards the concurrency limits. Instances in the `waiting` state are excluded from these limits.
[^7]: Only instances with a `running` state count towards the concurrency limits. Instances in the `waiting` state are excluded from these limits. Workers Paid cron-triggered Workflow instances have a separate one-hour cron concurrency budget per firing.

[^8]: Each instance created or restarted counts towards this limit

Expand Down Expand Up @@ -113,6 +113,12 @@ export class MyWorkflow extends WorkflowEntrypoint<Env> {

While a given Workflow instance is waiting for 30 days, it will transition to the `waiting` state, allowing other `queued` instances to run if concurrency limits are reached.

### Cron-triggered instances on Workers Paid

On [Workers Paid](/workers/platform/pricing/#workers), Workflow instances created by `schedules` can run for up to one hour per cron firing without consuming a Workflow concurrency slot.

After that budget is used, the instance yields and enters the normal concurrency queue. It resumes when a concurrency slot is available. The instance does not fail, time out, or terminate because it used this cron concurrency budget.

### Increasing Workflow step limits

Each Workflow instance supports 10,000 steps by default, but this can be increased up to 25,000 steps in your Wrangler configuration. Refer to [Workflow step limits](/workflows/build/workers-api/#workflow-step-limits) for more information.
Expand Down