Workflows grows up: scaling the control plane for machine-speed triggers
Workflows, Cloudflare’s durable execution engine, was originally built with human-driven triggers in mind — onboarding flows, order placements, and other events where the natural cadence is one instance per person, and people only move so fast. But as agents have evolved from experimental demos into persistent infrastructure, the access pattern has flipped. Workflows are now increasingly spawned by other software, not people, at a rate and volume that the original architecture was never designed to handle.
Agent sessions routinely kick off dozens of workflow instances as durable harnesses for their own loops and sub-tasks; with the Agents SDK integration and Project Think in the mix, that velocity is only accelerating. To prepare for that wave, Cloudflare has rebuilt the Workflows control plane and raised the platform limits accordingly:
- 50,000 concurrent instances per account, up from 4,500
- 300 instance creations per second per account, up from 100
- 2 million queued instances per workflow, up from 1 million
The old limits weren't arbitrary — they were a symptom of the V1 control plane's design. V2 replaces that architecture with a horizontally scalable one that distributes work and metadata across new components, then migrates every existing account onto it without downtime.
V1: a single point of serialization
export class MyWorkflow extends WorkflowEntrypoint {
async run(event, step) {
const data = await step.do("fetch-data", async () => {
return fetchFromAPI();
});
const approval = await step.waitForEvent("approval", {
type: "approval",
timeout: "24 hours",
});
await step.do("process-and-save", async () => {
return store(transform(data));
});
}
}
Workflows is built entirely on Cloudflare's own developer platform, using SQLite-backed Durable Objects as the primitive for coordination and storage. In V1, each workflow instance ran on its own Engine Durable Object, handling step execution, retries, and sleep logic. But the Account — a third, account-level Durable Object — was the central registry and coordinator for everything else.
That design was clean but brittle under load. Every create, update, and list operation had to pass through the Account DO. Customers running high-concurrency workloads could have thousands of instances starting and stopping at any moment, generating thousands of requests per second to a single object. The original rate limits were a direct consequence: they existed to protect that DO from being overwhelmed.
V2: distributing the control plane
Rearchitecting for scale meant starting from first principles about who owns what. In V2, the core tenant is that the Engine — the Durable Object running the instance — is the sole source of truth for that instance's existence. This closes a semantic gap in V1, where an instance could be queued before its Engine actually existed, leaving it in a bad state. The new Account singleton should store only minimal metadata and face an invariant maximum number of concurrent requests.
Two new components make this possible: SousChef and Gatekeeper.
SousChef acts as a lieutenant to the Account, handling metadata and lifecycle management for a subset of instances within a single workflow. Instead of one Account juggling everything, an account now has a distribution of SousChefs, each responsible for a slice. An added benefit: this gives per-workflow isolation within an account, not just per-account isolation as before.

The second component, Gatekeeper, distributes concurrency slots across all SousChefs in the account as a leasing mechanism. When an instance is created, it's assigned at random to a SousChef, which then checks with the Account for a slot. If one is granted, the SousChef triggers execution; if not, the instance waits. Crucially, SousChefs communicate with the Account on a fixed one-second cycle, batching all slot requests into a single JSRPC call. This keeps the instance creation rate from ever overwhelming the Account, and the periodic batching preserves fairness — awakened instances get priority over new ones, while each SousChef makes sure its own don't get stuck.
With this distributed architecture, the instance creation path is much lighter:
- Check the control plane version
- Check for a cached workflow and version definition in the local region; if absent, fetch from Account and cache it
- Store only essential metadata (payload, creation date) on the instance's own Engine
Registration with the control plane happens as a background task, but since background operations on a Durable Object can fail due to eviction or server errors, an alarm is also set on the Engine in the creation hot path. If the background task doesn't finish, the alarm guarantees the instance will still start. This combination of background tasks plus alarms — which have an at-least-once execution model with built-in retries — keeps instance creation fast without compromising reliability.
Beyond raw scale, the V2 control plane delivers other improvements: instance listing is faster and consistent with cursor pagination, any operation on an instance takes exactly one network hop straight to its Engine, and the system can more aggressively verify that instances run on time and correct any that fall behind.
Migrating without stopping traffic
Rolling out V2 was only half the job. By the time the rearchitecture was ready, Workflows had accumulated millions of instances and thousands of live customers. Compounding the difficulty: some V1 accounts had queued instances without corresponding Engine Durable Objects, a lingering effect of the old design's check gap.
// You might be wondering what's this SousChef class? This is the SousChef DO class!
import { SousChef } from "@repo/souschef";
class AccountOld extends DurableObject {
constructor(state: DurableObjectState, env: Env) {
// We added the following snippet to the end of our AccountOld DO's
// constructor. This ensures that if we want, we can use any primitive
// that is available on SousChef DO
if (this.currentVersion === ControlPlaneVersions.SOUS_CHEFS) {
this.sousChef = new SousChef(this.ctx, this.env);
await this.sousChef.setup()
}
}
async updateInstance(params: UpdateInstanceParams) {
if (this.currentVersion === ControlPlaneVersions.SOUS_CHEFS) {
assert(this.sousChef !== undefined, 'SousChef must exist on v2');
return this.sousChef.updateInstance(params);
}
// old logic remains the same
}
@RequiresVersion<AccountOld>(ControlPlaneVersions.V1)
async getMetadata() {
// this method can only be run if
// this.currentVersion === ControlPlaneVersions.V1
}
}
The migration strategy turned a constraint into an advantage. Existing Account Durable Objects — dubbed AccountOlds — were converted to behave as SousChefs. This worked cleanly because the SQL table schemas for instance metadata are identical across AccountOld and SousChef DOs; only the code class differs. Migrating millions of rows of metadata was thus unnecessary — just a matter of changing which class the DO instantiated.
The per-account cutover happened in roughly three concurrent steps:
- New instance creation requests were rerouted to newly spun-up SousChefs, meaning no new instances ever touched AccountOld again
- Existing AccountOld DOs began the self-migration to behave as SousChefs
- A fresh Account DO was created with its corresponding metadata
Once all accounts were on V2, the AccountOld DOs were kept alive only until their instance retention periods expired, then spun down permanently. The whole migration was done with zero downtime — a running wheel change, executed in production.
Operational limits and getting started
Workflows is available to try today. New users can start with the Get Started guide, or jump straight into building a durable agent.
The new control plane ships with higher default limits: a concurrency limit of 50,000 slots per workflow, and an account-level creation rate limit of 300 instances per second, with a per-workflow cap of 100 instances per second. If your workload needs more headroom than these defaults, you can request an increase through your account team or the Workers Limit Request Form.
For feedback, feature requests, or to share how you are using Workflows, join the Discord server.



