跳转到主要内容

Run a Linux command

You will POST uname -a to a Worker and read stdout that contains Linux.

Prerequisites

Run a command in Linux

  1. Create a Worker project:

  2. Change into the project directory:

    cd sandbox-linux
    
  3. Replace wrangler.jsonc so a Durable Object can start a container:

    {
    	"$schema": "node_modules/wrangler/config-schema.json",
    	"name": "sandbox-linux",
    	"main": "src/index.ts",
    	"compatibility_date": "$today",
    	"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",
    		},
    	},
    }
    
  4. Replace src/index.ts. The Worker reads argv from the JSON body and runs it in Linux:

    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("No container is 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));
    	},
    };
    
  5. Generate types for the binding. Wrangler reads the MyContainer class from src/index.ts to type env.MY_CONTAINER:

  6. Run wrangler dev:

    wrangler dev runs the instance in Docker on your machine, so Docker must be running. Running cloudflare/debian-trixie locally needs Wrangler 4.141.0 or later.

  7. 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 container and ran the command you sent. When you deploy, each instance runs in its own virtual machine instead of in Docker.

Next steps