CloudflareContainerBackend vs WorkerShellBackend vs WorkerJavaScriptBackend: Key Differences in the Computer SDK
The Cloudflare Computer SDK provides three distinct execution backends—Container, Worker-Shell, and Worker-JavaScript—that differ in runtime environment, filesystem handling, startup cost, and capabilities.
The Container backend spins up a full Linux container, while Worker-Shell runs a bash interpreter inside a Dynamic Worker, and Worker-JavaScript executes JS modules directly in the Worker isolate. Each backend is optimized for different workloads, from heavy native builds to fast text processing to structured JavaScript execution.
Runtime Environment Comparison
The fundamental distinction between these three backends lies in where and how your code executes.
Container Backend: Full Linux Container
The ContainerBackend creates a real Linux container that runs the computerd daemon. This provides a complete POSIX userland including bash, coreutils, arbitrary binaries, and network access.
In packages/computer/src/backends/container/index.ts, the backend wires the container daemon and defines a BackendHandle with sync: "remote"【📄】. This means the container maintains its own filesystem that must be synchronized with the host Durable Object.
Worker-Shell Backend: Bash in a Dynamic Worker
The WorkerShellBackend executes a just-bash interpreter inside a Cloudflare Dynamic Worker. No container is started. Instead, the shell forwards every filesystem call back to the host Durable Object.
According to the source code in packages/computer/src/backends/worker-shell/entrypoint.ts, this backend creates a Dynamic Worker running just-bash, registers built-in git and assets commands, and sets sync: "none"【📄】.
Worker-JavaScript Backend: Native JS Execution
The WorkerJavaScriptBackend runs JavaScript modules directly inside the Worker isolate. Unlike the shell backend, code is evaluated as a JavaScript module rather than a shell script, giving you a native-JS execution environment.
The implementation in packages/computer/src/backends/worker-javascript/module-graph.ts builds the module graph for JavaScript execution, marks the backend as callable, and also uses sync: "none"【📄】.
Filesystem and Sync Models
| Backend | Filesystem Handling | Sync Mode |
|---|---|---|
| ContainerBackend | Separate FUSE-backed SQLite VFS inside the container; remote push/pull to host DO | "remote" |
| WorkerShellBackend | All operations proxied to host DO's SQLite store; single authoritative source | "none" |
| WorkerJavaScriptBackend | File I/O proxied to host DO's SQLite store; same storage shape as shell backend | "none" |
The Container backend's remote sync mode requires explicit push/pull cycles to synchronize the container's VFS with the host Durable Object. Both Worker backends eliminate this overhead by treating the host DO as the sole source of truth.
Startup Cost and Scaling Characteristics
ContainerBackend incurs significant per-session overhead:
- Container creation takes seconds
- Per-session container charges apply
- Best for long-running or compute-intensive tasks
WorkerShellBackend offers instant, cheap scaling:
- Dynamic Worker cached per workspace (
workspace-shell:${workspace.id}) - Zero cold start for cached workspaces
- Ideal for high-frequency, short-duration operations
WorkerJavaScriptBackend shares the same scaling properties as Worker-Shell since both run in the Worker isolate without container overhead.
Capabilities and Command Sets
Container Backend Capabilities
Full Linux toolchain access:
- Run
npm install,yarn,pnpm - Compile native binaries with
gcc,clang,go - Invoke any binary on
$PATH - Network access for fetching dependencies
Worker-Shell Backend Capabilities
Limited to just-bash implemented commands:
- Text utilities:
cat,grep,awk,sed,sort,uniq,wc - Data processing:
jq,head,tail,cut - Custom commands:
git,assets(proxied to host DO)
Worker-JavaScript Backend Capabilities
Structured JavaScript execution:
- Execute arbitrary JS modules with ES imports
- Receive structured
inputvalues - Return structured
resultobjects - No shell command parsing required
Typical Use Cases
Choose the backend that matches your workload:
-
ContainerBackend — Heavy-weight workloads requiring a real Linux environment: building npm packages, running native binaries, complex build tools, isolated OS environments.
-
WorkerShellBackend — Textual processing, search, transformation, or summarization using shell utilities; scenarios demanding fast startup, cheap scaling, and isolation without container overhead.
-
WorkerJavaScriptBackend — Agent logic best expressed as JavaScript modules; custom business logic requiring library code; situations needing a callable backend that returns structured data rather than parsing stdout.
Code Examples
Container Backend
import { ContainerBackend } from "@cloudflare/computer/backends/container";
const workspace = new Workspace({
storage: ctx.storage,
backends: [
new ContainerBackend({
// optional: custom loader, env vars, etc.
})
]
});
await workspace.runtime.exec("npm install", { cwd: "/project" });
Worker-Shell Backend
import { WorkerShellBackend } from "@cloudflare/computer/backends/worker-shell";
const workspace = new Workspace({
storage: ctx.storage,
backends: [
new WorkerShellBackend({
loader: env.LOADER,
workspace: { binding: "Example", id: ctx.id.toString() },
ctx,
})
]
});
await workspace.runtime.exec("grep -R 'TODO' src/", { backend: "worker-shell" });
Worker-JavaScript Backend
import { WorkerJavaScriptBackend } from "@cloudflare/computer/backends/worker-javascript";
const workspace = new Workspace({
storage: ctx.storage,
backends: [
new WorkerJavaScriptBackend({
loader: env.LOADER,
// `callable: true` lets you pass/receive JSON values
})
]
});
const { result } = await workspace.runtime.exec({
source: `
export default async function (input) {
return { hello: "world", sum: input.a + input.b };
}
`,
input: { a: 3, b: 5 },
callable: true,
backend: "worker-javascript"
});
console.log(result); // → { hello: "world", sum: 8 }
Summary
-
ContainerBackend provides full Linux isolation with
sync: "remote"and per-session container overhead—ideal for native builds and complex toolchains. -
WorkerShellBackend delivers instant-starting bash execution with
sync: "none"and proxied filesystem access—perfect for text processing at scale. -
WorkerJavaScriptBackend enables structured JS module execution with
sync: "none"and callable interfaces—best for programmatic logic requiring typed inputs and outputs.
All three backends share the same Workspace abstraction and SQLite-backed storage, letting you mix backends within a single application based on task requirements.
Frequently Asked Questions
When should I choose ContainerBackend over the Worker backends?
ContainerBackend is the right choice when you need actual Linux capabilities: compiling native code, running binaries not available in a restricted environment, or using tools like Docker or complex build systems. The seconds-long startup and per-session cost are acceptable tradeoffs for these workloads. According to the Cloudflare Computer source code, this backend is implemented in packages/computer/src/backends/container/index.ts with sync: "remote" requiring explicit VFS synchronization【📄】.
Can I use npm packages with WorkerShellBackend?
No. WorkerShellBackend only exposes commands implemented by just-bash plus the custom git and assets commands. It cannot run npm, node, or install JavaScript dependencies. For npm-based workflows, you must use ContainerBackend or pre-bundle dependencies and execute them via WorkerJavaScriptBackend.
How does WorkerJavaScriptBackend handle module imports?
The backend builds a module graph in packages/computer/src/backends/worker-javascript/module-graph.ts【📄】. You can use standard ES module import syntax within your source string, and the loader resolves dependencies. The executed module receives a structured input object and must export a default function returning the structured result.
Do all backends share the same storage?
Yes. All three backends ultimately persist data to the same host Durable Object's SQLite store. The difference is how they access it: ContainerBackend maintains a separate VFS that syncs remotely, while both Worker backends proxy I/O directly with sync: "none" and no intermediate filesystem layer.
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 →