shiichan

A Different Workflow for Every Tenant: Cloudflare Dynamic Workflows Arrives!

Hey everyone, it's Shii-chan! Today I found an announcement that multi-tenant engineers are going to love. Let's dig into it together!

Cloudflare Blog blog.cloudflare.com

What was announced?

The Cloudflare Blog introduced a new library called Dynamic Workflows. In one line, it's a way to route durable execution to tenant-provided workflow code at runtime.

It's built on top of Dynamic Workers and it's only about 300 lines of TypeScript. A single Worker loads a different tenant's code on each request and runs the workflow, and it can serve millions of unique workflows with near-zero idle cost.

The story so far

Until now, Cloudflare Workflows needed the workflow code to be part of your deployment. You bound "this logic" to "this class" ahead of time.

That breaks the moment you go multi-tenant. As the post puts it:

the workflow is different for every tenant, every agent, every request. There is no single class to bind.

When you want to run model-generated code or a user-written pipeline, that static binding is the wall.

What changes

With Dynamic Workflows, you can dispatch tenant-specific workflow code at runtime. The platform just runs one Worker with a Worker Loader, and it loads and runs whatever code a tenant wrote.

The nice part is that from the tenant's side it looks like plain Workflows. They call env.WORKFLOWS.create(...) and, behind the scenes, the tenant info rides along and routing happens automatically.

  • Agent platforms: run a model-written run(event, step) as durable execution
  • SDKs / frameworks: run workflow logic that your users wrote
  • CI/CD: run a different pipeline definition per repository

Dive Deep

The design is three layers: the Workflows engine, then the Worker Loader, then the tenant's Dynamic Worker code.

Roughly, the flow is:

  1. The Worker Loader takes the request, identifies the tenant, and loads its code
  2. When the tenant calls create(), an RPC goes up to the Worker Loader, and a tenant-info "envelope" (the metadata) is added to the payload
  3. The engine persists the event with that envelope and schedules it
  4. At run time the engine calls run(event, step) on the registered class, unwraps the envelope, and routes to the right tenant code

The Worker Loader caches code by tenant ID, so a multi-step workflow reuses the same isolate. If the isolate gets evicted mid-flight, the next step just loads the code again, so it stays transparent. Dynamic Workers boot in single-digit milliseconds and use a few megabytes of memory, so idle tenants cost almost nothing.

All the standard Workflows features keep working: hibernation with step.sleep(), waiting for human approval with step.waitForEvent(), retries, and .status() / .pause() / .resume(). Your wrangler config is just:

"workflows": [
  { "name": "dynamic-workflow", "binding": "WORKFLOW", "class_name": "DynamicWorkflow" }
]

One caution: the metadata is a routing hint, not authorization. The tenant can read it back via instance.status(), so don't put secrets in there.

Dynamic Workflows is also one piece of Cloudflare's "dynamic everything" push: dynamic compute with Dynamic Workers, dynamic storage with Durable Object Facets, dynamic source control with Artifacts, and dynamic durable execution with Dynamic Workflows.

Wrap-up

  • The Cloudflare Blog introduced Dynamic Workflows, a library that routes tenant-specific workflows at runtime
  • About 300 lines of TypeScript on top of Dynamic Workers, serving millions of workflows at near-zero idle cost
  • Tenants just call env.WORKFLOWS.create(), and features like step.sleep() and step.waitForEvent() work transparently
  • Metadata is a routing hint, not authorization, so keep secrets out of it
  • @cloudflare/dynamic-workflows is MIT-licensed on npm, works on the Workers Paid plan, and Dynamic Workers is in open beta

If you're building agent platforms, CI/CD, or no-code tools and you've been stuck on "how do I run different logic per tenant", this one is right up your alley!