You will POST uname -a to a Worker and read stdout that contains Linux.
- Sign up for a Cloudflare account ↗︎.
- Install
Node.js↗︎.
Node.js version manager
Use a Node version manager like Volta ↗︎ or nvm ↗︎ to avoid permission issues and change Node.js versions. Wrangler, discussed later in this guide, requires a Node version of 16.17.0 or later.
-
Create a Worker project:
npm create cloudflare@latest -- sandbox-linux --category=hello-world --type=hello-world --lang=ts --no-deploy --no-git --no-agentsyarn create cloudflare sandbox-linux --category=hello-world --type=hello-world --lang=ts --no-deploy --no-git --no-agentspnpm create cloudflare@latest sandbox-linux --category=hello-world --type=hello-world --lang=ts --no-deploy --no-git --no-agents -
Change into the project directory:
cd sandbox-linux -
Replace
wrangler.jsoncso a Durable Object can start a container:{ "$schema": "node_modules/wrangler/config-schema.json", "name": "sandbox-linux", "main": "src/index.ts", // Set this to today's date "compatibility_date": "2026-10-05", "observability": { "enabled": true, }, "upload_source_maps": true, "containers": [ { "class_name": "MyContainer", "scheduling_policy": "durable_object", }, ], "durable_objects": { "bindings": [ { "class_name": "MyContainer", "name": "MY_CONTAINER", }, ], }, "exports": { "MyContainer": { "type": "durable-object", "storage": "sqlite", }, }, }"$schema" = "node_modules/wrangler/config-schema.json" name = "sandbox-linux" main = "src/index.ts" # Set this to today's date compatibility_date = "2026-10-05" upload_source_maps = true [observability] enabled = true [[containers]] class_name = "MyContainer" scheduling_policy = "durable_object" [[durable_objects.bindings]] class_name = "MyContainer" name = "MY_CONTAINER" [exports.MyContainer] type = "durable-object" storage = "sqlite" -
Replace
src/index.ts. The Worker readsargvfrom the JSON body and runs it in Linux:src/index.jsjs import { DurableObject } from "cloudflare:workers"; export class MyContainer extends DurableObject { async exec(argv) { const container = this.ctx.container; if (!container) { throw new Error("The container binding is not configured"); } if (!container.running) { container.start({ // Debian Trixie with Node.js 24 image: "cloudflare/debian-trixie", // Keep the instance running so it can accept commands entrypoint: ["sleep", "infinity"], // Block commands in the sandbox from reaching the Internet enableInternet: false, }); } const process = await container.exec(argv); const output = await process.output(); return { stdout: new TextDecoder().decode(output.stdout), exitCode: output.exitCode, }; } } export default { async fetch(request, env) { const { argv } = await request.json(); const sandbox = env.MY_CONTAINER.getByName("sandbox"); return Response.json(await sandbox.exec(argv)); }, };src/index.tsts import { DurableObject } from "cloudflare:workers"; export class MyContainer extends DurableObject<Env> { async exec(argv: string[]) { const container = this.ctx.container; if (!container) { throw new Error("The container binding is not configured"); } if (!container.running) { container.start({ // Debian Trixie with Node.js 24 image: "cloudflare/debian-trixie", // Keep the instance running so it can accept commands entrypoint: ["sleep", "infinity"], // Block commands in the sandbox from reaching the Internet enableInternet: false, }); } const process = await container.exec(argv); const output = await process.output(); return { stdout: new TextDecoder().decode(output.stdout), exitCode: output.exitCode, }; } } export default { async fetch(request: Request, env: Env): Promise<Response> { const { argv } = (await request.json()) as { argv: string[] }; const sandbox = env.MY_CONTAINER.getByName("sandbox"); return Response.json(await sandbox.exec(argv)); }, }; -
Generate types for the binding. Wrangler reads the
MyContainerclass fromsrc/index.tsto typeenv.MY_CONTAINER:npx wrangler typesyarn wrangler typespnpm wrangler types -
Run
wrangler dev:npx wrangler devyarn wrangler devpnpm wrangler devwrangler devruns the instance in Docker ↗︎ on your machine, so Docker must be running. Runningcloudflare/debian-trixielocally needs Wrangler 4.141.0 or later. -
POST a command to the URL Wrangler prints. The default is
http://localhost:8787:curl http://localhost:8787 --request POST --json '{"argv":["uname","-a"]}'
The JSON body includes "exitCode":0. stdout contains Linux. The Worker started a Linux VM and ran the command you sent.
- Run another process in the instance. Refer to
exec(). - Deploy your Worker. Refer to Deploy Containers. A deploy does not replace a running instance. Refer to Sandbox lifetime.
- Run JavaScript. Refer to Run JavaScript.
- Use the Durable Object container API for
start()andexec(). - Start a project from the minimal sandbox template ↗︎. It adds a
Dockerfile, a sandbox for each name in the URL, and file reads and writes with@cloudflare/sandbox.