Getting started
You can create a Worker that spins up other Workers, called Dynamic Workers, at runtime to execute code on-demand in a secure, sandboxed environment. You provide the code, choose which bindings the Dynamic Worker can access, and control whether the Dynamic Worker can reach the network.
Dynamic Workers support two loading modes:
load(code)creates a fresh Dynamic Worker for one-time execution.get(id, callback)caches a Dynamic Worker by ID so it can stay warm across requests.
load() is best for one-time code execution, for example when using Codemode. get(id, callback) is better when the same code will receive subsequent requests, for example when you are building applications.
Use this "hello world" starter ↗ to get a Worker deployed that can load and execute Dynamic Workers.
You can also deploy the Dynamic Workers Playground ↗, where you can write or import code, bundle it at runtime with @cloudflare/worker-bundler, execute it through a Dynamic Worker, and see real-time responses and execution logs.
In order for a Worker to be able to create Dynamic Workers, it needs a Worker Loader binding. Unlike most Workers bindings, this binding doesn't point at any external resource in particular; it simply provides access to the Worker Loader API.
Configure it like so, in your Worker's wrangler.jsonc:
{ "worker_loaders": [ { "binding": "LOADER", }, ],}[[worker_loaders]]binding = "LOADER"Your Worker will then have access to the Worker Loader API via env.LOADER.
Use env.LOADER.load() to create a Dynamic Worker and run it:
export default { async fetch(request, env) { // Load a worker. const worker = env.LOADER.load({ compatibilityDate: "$today",
mainModule: "src/index.js", modules: { "src/index.js": ` export default { fetch(request) { return new Response("Hello from a dynamic Worker"); }, }; `, },
// Block all outbound network access from the Dynamic Worker. globalOutbound: null, });
// Get the Dynamic Worker's `export default` entrypoint. // (A Worker can also export separate, named entrypoints.) let entrypoint = worker.getEntrypoint();
// Forward the HTTP request to it. return entrypoint.fetch(request); },};export default { async fetch(request: Request, env: Env): Promise<Response> { // Load a worker. const worker = env.LOADER.load({ compatibilityDate: "$today",
mainModule: "src/index.js", modules: { "src/index.js": ` export default { fetch(request) { return new Response("Hello from a dynamic Worker"); }, }; `, },
// Block all outbound network access from the Dynamic Worker. globalOutbound: null, });
// Get the Dynamic Worker's `export default` entrypoint. // (A Worker can also export separate, named entrypoints.) let entrypoint = worker.getEntrypoint();
// Forward the HTTP request to it. return entrypoint.fetch(request); },};In this example, env.LOADER.load() creates a Dynamic Worker from the code defined in modules and returns a stub that represents it.
worker.getEntrypoint().fetch(request) sends the incoming request to the Dynamic Worker's fetch() handler, which processes it and returns a response.
If you expect to load the exact same Worker more than once, use get(id, callback) instead of load(). The id should be a unique string identifying the particular code you intend to load. When the runtime sees the same id again, it can reuse the existing Worker instead of creating a new one, if it hasn't been evicted yet.
The callback you provide will only be called if the Worker is not already loaded. This lets you skip loading the code from storage when the Worker is already running.
const worker = env.LOADER.get("hello-v1", async () => { // Callback only runs if there is not already a warm // instance available.
// Load code from storage. let code = await env.MY_CODE_STORAGE.get("hello-v1");
// Return the same format as `env.LOADER.load()` accepts. return { compatibilityDate: "$today", mainModule: "index.js", modules: { "index.js": code }, globalOutbound: null, };});const worker = env.LOADER.get("hello-v1", async () => { // Callback only runs if there is not already a warm // instance available.
// Load code from storage. let code = await env.MY_CODE_STORAGE.get("hello-v1");
// Return the same format as `env.LOADER.load()` accepts. return { compatibilityDate: "$today", mainModule: "index.js", modules: { "index.js": code, }, globalOutbound: null, };});Dynamic Workers support JavaScript (ES modules and CommonJS) and Python. The code is passed as strings in the modules object. There is no build step, so languages like TypeScript must be compiled to JavaScript before being passed to load() or get().
For the full list of supported module types, refer to the API reference.
If your Dynamic Worker needs TypeScript compilation or npm dependencies, the code must be transpiled and bundled before passing to the Worker Loader.
@cloudflare/worker-bundler ↗ is a library that handles this for you. Use it to bundle source files into a format that load() and get() accept:
import { createWorker } from "@cloudflare/worker-bundler";
const worker = env.LOADER.get("my-worker", async () => { const { mainModule, modules } = await createWorker({ files: { "src/index.ts": ` import { Hono } from 'hono'; const app = new Hono(); app.get('/', (c) => c.text('Hello from Hono!')); export default app; `, "package.json": JSON.stringify({ dependencies: { hono: "^4.0.0" }, }), }, });
return { mainModule, modules, compatibilityDate: "2026-01-01" };});createWorker() handles TypeScript compilation, dependency resolution from npm, and bundling. It returns mainModule and modules ready to pass directly to load() or get().