Skip to content

CLOUDFLARE 2026-05-01 Tier 1

Read original ↗

Cloudflare — Introducing Dynamic Workflows: durable execution that follows the tenant

Summary

Cloudflare launches @cloudflare/dynamic-workflows — a small (~300 LOC) MIT-licensed TypeScript library that bridges Cloudflare Workflows (durable execution) with Dynamic Workers (per-request V8-isolate sandboxing). Until this post, a Workflow's class_name in wrangler.jsonc was statically bound to a single class per deploy — fine if you own all the code, broken the moment you want each tenant, agent, or repository to ship their own run(event, step) function. Dynamic Workflows lifts that constraint: a single Worker Loader dispatches each create() call and each subsequent run(event, step) invocation into the right tenant's code, and the Workflows engine's durability machinery (IDs, step.sleep(), step.waitForEvent(), retries, hibernation) continues to work unchanged. The library is envelope-and-unwrap glue around .create() on the outbound side and around WorkflowEntrypoint on the inbound side; the real work — spinning up the tenant's code, sandboxing it, routing RPC across the boundary, caching and hibernating isolates — is delegated to Dynamic Workers underneath. Cloudflare frames Dynamic Workflows as the third instance of a generalising pattern (after Durable Object Facets for storage) and pre-announces the same shape for queues, caches, databases, object stores, AI bindings, and MCP servers. The post uses CI/CD as its canonical showcase: every repo ships its own CIPipeline class in .cloudflare/ci.ts, the platform's webhook dispatcher loads it as a Dynamic Worker, hands execution off to Dynamic Workflows, and leverages Artifacts + ArtifactFS + Sandboxes for the heavy corners — eliminating the traditional allocate VM → pull image → git clone → npm ci → run → tear down ceremony.

Key takeaways

  1. Static binding was the last missing piece. wrangler.jsonc historically bound class_name to exactly one Workflow class per deploy: "One binding, one class. Per deploy." That works if you own all the code; it breaks the moment an app-platform, CI/CD product, or agents SDK wants each tenant / repo / agent to ship their own workflow. Dynamic Workflows gives that use case a clean primitive. (Source: Dynamic Workflows post.)
  2. Core shape: wrap .create() outbound, wrap WorkflowEntrypoint inbound. Every load-bearing line of the library is one of those two wrappers. The tenant calls env.WORKFLOWS.create(...) against what looks like a normal Workflow binding (wrapWorkflowBinding({ tenantId })); the library transparently rewrites the payload into { __workerLoaderMetadata: { tenantId }, params: <original> } and forwards to the real WORKFLOWS binding. When the engine wakes the workflow — seconds, hours, or days later — the envelope rides along with the persisted payload and routes back to the right tenant's code. Canonicalised here as patterns/metadata-envelope-in-durable-payload and concepts/envelope-wrap-and-unwrap-metadata-routing.
  3. Bindings that cross the Dynamic Worker boundary must be RPC stubs. "A plain { create, get } object can't be structured-cloned, and the raw Workflow binding isn't serializable either." The wrapped binding is a WorkerEntrypoint subclass (DynamicWorkflowBinding) that the runtime specialises with the tenant's metadata at load time — which is why you must export { DynamicWorkflowBinding } from your Worker Loader so the runtime can look the class up in cloudflare:workers exports. (Source: sources/2026-05-01-cloudflare-introducing-dynamic-workflows-durable-execution-that-follows-the-tenant.)
  4. Three-layer dispatch: engine → Worker Loader → tenant code. Request reaches the Worker Loader → loads the tenant's code as a Dynamic Worker → tenant's default.fetch receives the request with a wrapped WORKFLOWS binding → tenant calls WORKFLOWS.create(...) (RPC to Worker Loader) → Worker Loader rewrites the payload and calls the real Workflows engine → engine persists event.payload (including envelope) → later, engine calls run(event, step) on the registered class → that class (createDynamicWorkflowEntrypoint-generated) unwraps the envelope, calls the loadRunner callback you wrote, routes to the right tenant runner. Canonicalised here as concepts/per-tenant-dynamic-code-dispatch.
  5. Metadata is a routing hint, not authorization. "The tenant can read it back via instance.status(). Don't put secrets in there." The envelope pattern is a wire-format concern; tenant isolation and authorization remain the Worker Loader's responsibility via Dynamic Workers' capability-based bindings (concepts/capability-based-sandbox). (Source: sources/2026-05-01-cloudflare-introducing-dynamic-workflows-durable-execution-that-follows-the-tenant.)
  6. The loadRunner callback is the extension point. The createDynamicWorkflowEntrypoint(async ({ env, metadata }) => ...) callback is where "everything interesting happens, and it's entirely yours": fetch the tenant's latest source from R2, check their plan tier and pick a region, attach a tail Worker for per-tenant logging, bundle TypeScript on the fly with @cloudflare/worker-bundler. In the common case you just return stub.getEntrypoint('TenantWorkflow'). Canonicalised here as concepts/byo-workflow-per-tenant.
  7. Isolate caching + hibernation make dispatch overhead free. "A Dynamic Worker boots in single-digit milliseconds using a few megabytes of memory, so the dispatch overhead is essentially free. You can have a million tenants, each with their own distinct workflow code, each spun up lazily on the step boundary where it's needed, and none of them cost anything while idle." The Worker Loader caches by ID so a workflow that runs many steps over many hours reuses the same Dynamic Worker; when the isolate is evicted, the next step.do() pulls the code again and keeps going, invisible to the workflow. (Source: sources/2026-05-01-cloudflare-introducing-dynamic-workflows-durable-execution-that-follows-the-tenant.)
  8. Lower-level primitive for advanced cases. If you want to subclass WorkflowEntrypoint yourself — to add logging around run(), wire up per-tenant observability, or thread custom state through — the library exposes dispatchWorkflow as the callable primitive that createDynamicWorkflowEntrypoint is built on. You keep the class declaration; you get the same metadata unwrap + loadRunner dispatch mechanics as a library call.
  9. Workflows V2 capacity context. Dynamic Workflows is "redesigned for the agentic era" atop the Workflows V2 rewrite — up to 50,000 concurrent instances and 300 new instances per second, per account. These numbers appear in the post as ambient capacity context for a single-account agents platform or CI product running per-tenant workflows at fleet scale. (Source: sources/2026-05-01-cloudflare-introducing-dynamic-workflows-durable-execution-that-follows-the-tenant.)
  10. Dynamic Workers is the primitive that "swallows everything". Cloudflare frames three instances of the same pattern: Durable Object Facets (storage), Dynamic Workflows (durable execution), and static Dynamic Workers (compute). "Every binding that Workers currently exposes is heading for a dynamic counterpart — queues where each producer ships its own handler, caches, databases, object stores, AI bindings, and MCP servers where every tenant brings their own tools." Canonicalised here as patterns/dynamic-binding-over-static-binding.
  11. Unit-economics claim. "A platform that used to cap out at thousands of paying customers can now reasonably serve tens of millions." The thesis is that isolate-level multi-tenancy + zero idle cost drops the per-tenant floor several orders of magnitude compared to the traditional container + database + disk + scheduler per customer shape. Asserted without a specific cost disclosure; treat as a directional claim, not a measured one.
  12. Canonical showcase: CI/CD as per-repo durable workflow. The customer ships .cloudflare/ci.ts with their own CIPipeline extends WorkflowEntrypoint class; the platform ingests a webhook, loads that repo's pipeline as a Dynamic Worker, and hands execution to Dynamic Workflows. Artifacts + ArtifactFS fork() gives each run its own isolated repo copy (seconds, not a git clone tax); Dynamic Workers runs each lightweight step (lint, test, build); Sandboxes handle heavy corners (docker build, integration suites, Rust compiles); Dynamic Workflows holds the run together with retryable steps, free hibernation waiting on approvals, survival across deploys/evictions. Canonicalised here as patterns/ci-pipeline-as-customer-authored-durable-workflow.
  13. Traditional CI vs. new-stack latency narrative. Allocate VM (15-30s) → pull base image (10s) → git clone (10s) → npm ci (30-60s) → run tests → tear down becomes edge fork of the repo (seconds) → each step boots a fresh isolate or snapshot-restored sandbox in milliseconds → hibernates. Qualitative, no p50/p99 numbers disclosed, but the key architectural claim is "the repo doesn't move — the compute comes to it."
  14. Composes with the agent-autonomy stack. For coding agents — OpenCode, Claude Code, Codex, Pi — Dynamic Workflows is "the piece that lets that plan be a first-class Cloudflare Workflow": an agent literally writes a run(event, step) body, the platform runs it with full durability (step.do() retryable, step.sleep('24 hours') free hibernation, step.waitForEvent() indefinite waits for human approval). Extends Project Think's fiber-based durable execution model with a workflow-as-artifact primitive the model itself can emit.

Operational numbers

  • Library size: ~300 lines of TypeScript.
  • Workflows V2 per-account capacity: 50,000 concurrent workflow instances; 300 new instances per second.
  • Dynamic Worker startup: single-digit milliseconds.
  • Dynamic Worker memory: a few megabytes per isolate.
  • Traditional CI VM allocation: 15-30s (claimed).
  • Base-image pull: ~10s (claimed).
  • git clone: ~10s (claimed).
  • npm ci: 30-60s (claimed).
  • Multi-GB repo hydration via ArtifactFS: "single-digit seconds" (claimed).
  • Packaging: MIT-licensed; npm install @cloudflare/dynamic-workflows; runs on Workers Paid plan atop Dynamic Workers open beta. Working example is an interactive browser playground in the dynamic-workflows GitHub repo.

Caveats / what the post does not disclose

  • No p50/p99 latency numbers for the dispatch hop; the "essentially free" claim is qualitative.
  • No per-isolate cost disclosure; the tens-of-millions-of-tenants unit-economics claim is directional, not measured.
  • No details on how the Workflows V2 50k-concurrent / 300-per-second limits interact with per-tenant dispatch (does each tenant's workflow count as a separate instance? The post implies yes but doesn't call it out).
  • No comparison against prior-art external dispatchers (Temporal + dynamic task queues, Cadence, serverless-workflow @ref-loaded definitions).
  • No discussion of the @cloudflare/worker-bundler internals that make bundle TypeScript on the fly work — referenced only in passing.
  • No discussion of tenant code update semantics mid-workflow: if a workflow sleeps for 24 hours and the tenant redeploys in the interim, does run(event, step) resume on the new code or the old code? The cache-by-ID comment implies eviction brings in the new code; the determinism implications for replay (concepts/workflow-determinism-requirement) are not discussed.
  • No discussion of how per-tenant observability surfaces work in practice (tail Worker is mentioned as an example, but without a concrete integration shape).
  • No disclosure of production adopters at launch — this is Day 1 of the library; CI/CD "here's what it would look like" is a design sketch, not a deployed reference architecture.

Systems

Concepts

Patterns

Cross-source continuity

  • Cloudflare 2026-04 → 2026-05 Agents-Week / durable-execution arc. Sibling to the 2026-04-15 Project Think post (fibers + sub-agents + Tier-1 Dynamic Workers / Tier-0 DO filesystem), the 2026-04-15 Agent Lee post (DO as credentialed-proxy boundary), the 2026-04-30 agent-provisioning protocol post (OAuth + payment token + auto-provisioned account), and the broader 2026-04 Cloudflare Agents-Week arc. 2026-05-01 Dynamic Workflows closes the durability gap for tenant-authored workflows, making the agent-written plan a first-class Workflow rather than an in-memory fiber.
  • Durable-execution three-shape taxonomy (as canonicalised on overview 2026-04-30). Three shapes so far: (a) external cluster + event history — Temporal / Cadence / Instacart Maple; (b) embedded library + state-field replay in-service — Airbnb Skipper; (c) actor-in-fiber + sub-agent spawning — Cloudflare Project Think (DO + JS Promise-like fibers). Dynamic Workflows doesn't introduce a fourth shape — it sits between (a) and (c) as a platform-hosted-engine with dispatched-per-tenant-code variant — but it does canonicalise a distinct dispatch-layer concern ("the engine runs; the code is dispatched") that the prior shapes left implicit.
  • The generalising-pattern arc. The post names three instances of the same "dynamic counterpart" shape Cloudflare is rolling out: (i) Dynamic Workers (compute, shipped 2026-04 open beta); (ii) Durable Object Facets (storage, shipped alongside Dynamic Workers); (iii) Dynamic Workflows (durable execution, this post). Pre-announces queues, caches, databases, object stores, AI bindings, and MCP servers as follow-ons. First wiki instance that names this shape as a deliberate platform strategy (rather than as three independent launches).
  • CI-primitive convergence arc. The CI/CD showcase intersects prior wiki coverage on three axes: (1) sources/2026-04-29-atlassian-inside-atlassians-merge-queues (validate-against-future-state-of-main; CI as merge boundary); (2) sources/2026-04-24-atlassian-rovo-dev-driven-development (agent-authored PRs; Fireworks Firecracker-microVM substrate); (3) systems/github-actions-like model. Dynamic Workflows proposes a fourth axis — CI as customer-ships-the-pipeline durable workflow — with Cloudflare-specific primitives replacing the VM allocation / base image pull / git clone / package install stages with edge-forked repos + per-step isolates.
  • Agents-SDK / Project Think continuity. The agents-that-plan- like-engineers subsection restates the "LLMs are better at writing code than making sequential tool calls" thesis from Project Think and Agent Lee, and positions Dynamic Workflows as the execution primitive that makes agent-authored plans durably executable rather than in-memory fibers only.
  • Workflows V2 capacity disclosure. The post is the first wiki instance that cites the Workflows V2 50k-concurrent / 300-new- per-second per-account limits. Prior wiki had Workflows as "long-running orchestration" with no capacity envelope.

Tier-1 on-scope rationale

Cloudflare is Tier 1 on the sysdesign-wiki. This post passes scope decisively: explicit library architecture (300 LOC wrapper over Dynamic Workers + Workflows V2), explicit three-layer dispatch topology (engine / Worker Loader / tenant code), explicit wire- format design (envelope-metadata in persisted payload), explicit RPC-vs-structured-clone architectural decision (DynamicWorkflowBinding must be an exported WorkerEntrypoint subclass), operational- parameter disclosure (single-digit-ms isolate boot, few-MB memory, 50k concurrent / 300-new-per-second Workflows V2 caps), trade-off framing (routing-metadata is not authorization; capability- based sandbox is the load-bearing isolation substrate), and an end-to-end CI/CD showcase with step-level mechanism disclosure. Architecture + protocol-design density ≫ 20% of body. Not product-PR — it is a launch post with substantive architecture, explicitly named by AGENTS.md as an include-not-skip shape.

Source

Last updated · 438 distilled / 1,268 read