diff --git a/src/content/changelog/workflows/2026-05-13-cron-workflows.mdx b/src/content/changelog/workflows/2026-05-13-cron-workflows.mdx new file mode 100644 index 000000000000000..b5932eb15814c31 --- /dev/null +++ b/src/content/changelog/workflows/2026-05-13-cron-workflows.mdx @@ -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/). diff --git a/src/content/docs/learning-paths/workflows-course/series/workflows-3.mdx b/src/content/docs/learning-paths/workflows-course/series/workflows-3.mdx index 3e4a708a58d11d4..2c12be8cc5195df 100644 --- a/src/content/docs/learning-paths/workflows-course/series/workflows-3.mdx +++ b/src/content/docs/learning-paths/workflows-course/series/workflows-3.mdx @@ -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"; -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. @@ -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: + + + + ```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) + - 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 diff --git a/src/content/docs/workers/configuration/cron-triggers.mdx b/src/content/docs/workers/configuration/cron-triggers.mdx index 7c3744ba9e070a0..d7fa05ff835ada4 100644 --- a/src/content/docs/workers/configuration/cron-triggers.mdx +++ b/src/content/docs/workers/configuration/cron-triggers.mdx @@ -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. ::: diff --git a/src/content/docs/workers/wrangler/configuration.mdx b/src/content/docs/workers/wrangler/configuration.mdx index f32639e194e9391..b1a1cd895f75f4a 100644 --- a/src/content/docs/workers/wrangler/configuration.mdx +++ b/src/content/docs/workers/wrangler/configuration.mdx @@ -1194,6 +1194,11 @@ To bind Workflows to your Worker, assign an array of the below object to the `wo - `script_name` - 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` + - 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: @@ -1205,6 +1210,7 @@ Example: "binding": "", "name": "", "class_name": "", + "schedules": [{ "cron": "0 * * * *" }], }, ], } diff --git a/src/content/docs/workflows/build/trigger-workflows.mdx b/src/content/docs/workflows/build/trigger-workflows.mdx index 0fe5cfe2ae25ecd..165cedff59cc1d1 100644 --- a/src/content/docs/workflows/build/trigger-workflows.mdx +++ b/src/content/docs/workflows/build/trigger-workflows.mdx @@ -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/) @@ -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: + + + +```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 * * * *" }] + } + ] +} +``` + + + +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 diff --git a/src/content/docs/workflows/examples/backup-d1.mdx b/src/content/docs/workflows/examples/backup-d1.mdx index a22c45b46fda50d..61854ad56cdc382 100644 --- a/src/content/docs/workflows/examples/backup-d1.mdx +++ b/src/content/docs/workflows/examples/backup-d1.mdx @@ -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. @@ -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 { - async run(event: WorkflowEvent, step: WorkflowStep) { - const { accountId, databaseId } = event.payload; +export class backupWorkflow extends WorkflowEntrypoint { + async run(_event: WorkflowEvent, 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"; @@ -105,18 +102,6 @@ export default { async fetch(req: Request, env: Env): Promise { 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}`); - }, }; ``` @@ -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/): @@ -143,11 +130,16 @@ 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": [ @@ -155,13 +147,12 @@ Here is a [Wrangler configuration file](/workers/wrangler/configuration/): "binding": "BACKUP_BUCKET", "bucket_name": "d1-backups" } - ], - "triggers": { - "crons": [ - "0 0 * * *" - ] - } + ] } ``` + +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. diff --git a/src/content/docs/workflows/get-started/guide.mdx b/src/content/docs/workflows/get-started/guide.mdx index d1705c6cf923684..abfbf14dab3a8eb 100644 --- a/src/content/docs/workflows/get-started/guide.mdx +++ b/src/content/docs/workflows/get-started/guide.mdx @@ -168,11 +168,38 @@ Follow the steps below to learn how to build a Workflow from scratch. } ``` - + - 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: + + + + ```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 * * * *" }] + } + ] + } + ``` + + + + 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: diff --git a/src/content/docs/workflows/reference/limits.mdx b/src/content/docs/workflows/reference/limits.mdx index a23c1e02c171623..f3b86701e2a62da 100644 --- a/src/content/docs/workflows/reference/limits.mdx +++ b/src/content/docs/workflows/reference/limits.mdx @@ -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 @@ -113,6 +113,12 @@ export class MyWorkflow extends WorkflowEntrypoint { 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.