How to Enable Command and Code Execution by Adding Backends to Workspace in Cloudflare Computer
To enable command and code execution in a Cloudflare Computer Workspace, add backends to the backends array when initializing the Workspace using the withWorkspace mix-in, then invoke workspace.runtime.exec() with the backend option to route execution to Container, Shell, or JavaScript runtimes.
Cloudflare Computer's Workspace provides a virtual filesystem that can execute shell commands and evaluate code through pluggable backends. Each backend implements a distinct execution surface—ranging from lightweight shells to full Linux containers—and is registered under a stable identifier when the workspace is created. This architecture allows developers to enable command and code execution by adding backends to Workspace configurations according to their specific runtime requirements.
Understanding Workspace Backend Architecture
The Workspace architecture separates storage from execution, allowing multiple backends to coexist without interference.
The WorkspaceBackend Interface
Every backend must conform to the WorkspaceBackend interface defined in the RPC layer at [packages/rpc/src/interface.ts](https://github.com/cloudflare/computer/blob/main/packages/rpc/src/interface.ts). This contract ensures that all backends expose a consistent surface for the Workspace to invoke. Internally, sync cursors are scoped per-backend (see the watermarks implementation in [packages/dofs/src/sync/watermarks.ts](https://github.com/cloudflare/computer/blob/main/packages/dofs/src/sync/watermarks.ts)), enabling concurrent operations across different execution environments without state collision.
Runtime Execution Flow
When you call workspace.runtime.exec(source, { backend }), the system resolves the execution target via workspaceBackendForCommand. The backend option selects which registered backend handles the request; if omitted, the runtime defaults to the first backend in the array. The selected backend receives the source—whether a shell string or JavaScript module—and executes it within its isolated environment.
Available Backend Types
Cloudflare Computer provides three primary backend implementations, each optimized for different workload characteristics.
Container Backend
The Container backend projects the workspace's SQLite state into a sandbox container and mounts it via FUSE, providing a full Linux userland capable of running any shell command or binary. This backend is ideal for heavy-weight operations requiring system packages or native binaries. Implementation resides in [packages/computer/src/backends/container/index.ts](https://github.com/cloudflare/computer/blob/main/packages/computer/src/backends/container/index.ts).
Worker-Shell Backend
The Worker-Shell backend runs the lightweight just-bash shell inside a Dynamic Worker. It forwards shell.exec calls to that worker while maintaining all state inside the original Durable Object, offering a fast, lightweight option for simple shell operations. Implementation resides in [packages/computer/src/backends/worker-shell/worker-shell.ts](https://github.com/cloudflare/computer/blob/main/packages/computer/src/backends/worker-shell/worker-shell.ts).
Worker-JavaScript Backend
The Worker-JavaScript backend evaluates ECMAScript modules in a Dynamic Worker, exposing a structured RPC surface including fs, git, and artifacts APIs. This backend is optimal for JavaScript/TypeScript automation scripts that need structured access to workspace resources. Implementation resides in [packages/computer/src/backends/worker-javascript/worker-javascript.ts](https://github.com/cloudflare/computer/blob/main/packages/computer/src/backends/worker-javascript/worker-javascript.ts).
Adding Backends to Your Workspace
You add backends by passing a backends array to the withWorkspace mix-in when defining your Durable Object class. The mix-in stores this array on the Workspace prototype, and the runtime automatically connects to a backend the first time it is addressed.
import { withWorkspace } from "@cloudflare/computer";
import { WorkerShellBackend } from "@cloudflare/computer/backends/worker-shell";
import { CloudflareContainerBackend } from "@cloudflare/computer/backends/container";
import curl from "@cloudflare/computer/shell/curl";
import jq from "@cloudflare/computer/shell/jq";
export class MyDO extends withWorkspace(
class extends DurableObject<Env> {},
(self) => {
const { ctx, env } = self as unknown as { ctx: DurableObjectState; env: Env };
return {
storage: ctx.storage as unknown as DurableObjectStorageLike,
backends: [
// Shell backend for quick commands
new WorkerShellBackend({
loader: env.LOADER,
workspace: { binding: "MyDO", id: ctx.id.toString() },
ctx,
commands: [curl, jq],
}),
// Container backend for full Linux environment
new CloudflareContainerBackend({
workspace: { binding: "MyDO", id: ctx.id.toString() },
env,
}),
],
};
}
) {}
Source: [examples/worker-shell/src/index.ts](https://github.com/cloudflare/computer/blob/main/examples/worker-shell/src/index.ts)
Executing Commands and Code
Once backends are registered, you route execution to specific backends using the backend option in workspace.runtime.exec().
Running Shell Commands
Target the shell backend (identified as 'shell' by default) to execute bash commands:
import { getWorkspace } from "@cloudflare/computer";
const ws = await getWorkspace(env.MyDO);
const result = await ws.runtime.exec('ls -la /workspace', { backend: 'shell' });
console.log(result.stdout);
Evaluating JavaScript Modules
Target the JavaScript backend to evaluate ECMAScript modules with access to workspace APIs:
import { getWorkspace } from "@cloudflare/computer";
const ws = await getWorkspace(env.MyDO);
const source = `
export default async function () {
const { readFile } = await import('node:fs/promises');
const data = await readFile('/workspace/example.txt', 'utf8');
return { data };
}
`;
const { result } = await ws.runtime.exec(source, { backend: 'javascript' });
console.log(result.data);
Executing Container Commands
Use the container backend for operations requiring system packages or heavy computation:
const ws = await getWorkspace(env.MyDO);
await ws.runtime.exec('apt-get update && apt-get install -y ffmpeg', {
backend: 'container',
});
Error Handling and Backend Selection
When workspace.runtime.exec is invoked, the runtime helper workspaceBackendForCommand (demonstrated in the think-compare-runtimes example) resolves the appropriate backend based on the provided option. Errors thrown during execution are wrapped as WorkspaceFsError instances with a code field, allowing callers to branch on specific error conditions like ENOENT or EUNKNOWN_HASH.
Summary
- Backend Architecture: Cloudflare Computer Workspaces support multiple execution surfaces through the
WorkspaceBackendinterface, with per-backend sync cursors ensuring isolation. - Registration: Add backends via the
backendsarray in thewithWorkspacemix-in options, supplying configuration for each backend type. - Execution: Route commands to specific backends using
workspace.runtime.exec(source, { backend: 'shell' | 'javascript' | 'container' }). - Error Handling: Execution failures return
WorkspaceFsErrorobjects with specific error codes for programmatic handling. - Key Files: Core logic resides in [
packages/computer/src/workspace.ts](https://github.com/cloudflare/computer/blob/main/packages/computer/src/workspace.ts), with backend implementations inpackages/computer/src/backends/.
Frequently Asked Questions
How do I choose between the Worker-Shell and Container backends?
Use the Worker-Shell backend for lightweight, fast-executing bash commands that don't require system packages, as it runs just-bash inside a Dynamic Worker with minimal overhead. Use the Container backend when you need a full Linux userland, system package managers like apt-get, or native binaries, as it provides a sandboxed container environment via FUSE.
Can I use multiple backends simultaneously in the same Workspace?
Yes, the Workspace architecture supports multiple concurrent backends. Each backend maintains independent sync cursors (as implemented in [packages/dofs/src/sync/watermarks.ts](https://github.com/cloudflare/computer/blob/main/packages/dofs/src/sync/watermarks.ts)), allowing them to coexist without interfering with each other's progress. You can route different commands to different backends within the same Durable Object instance.
What is the default backend if I don't specify one in exec()?
If you omit the backend option in workspace.runtime.exec(), the runtime uses the first backend in the backends array as the default. This is determined by the order in which you instantiate backends when configuring the withWorkspace mix-in.
How do I add custom shell commands to the Worker-Shell backend?
Pass an array of command modules to the commands property when instantiating WorkerShellBackend. For example, import curl and jq from @cloudflare/computer/shell/* and include them in the configuration object, as shown in the examples/worker-shell/src/index.ts implementation.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →