Isolate JavaScript Backend Limitations vs. Container Backend in Cloudflare Computer
The Isolate JavaScript backend in Cloudflare Computer enforces strict sandboxing with bounded concurrency, no native code execution, and restricted network access, while the Container backend provides full Linux containers with unlimited concurrency, native binary support, and direct filesystem access.
The Cloudflare Computer SDK allows developers to execute code within a Workspace using two distinct backend architectures. Understanding the architectural trade-offs between the Isolate (WorkerJavaScriptBackend) and Container (CloudflareContainerBackend) execution environments is critical for selecting the appropriate runtime for your workload.
Core Architectural Differences
The two backends diverge fundamentally in how they isolate and execute user code.
Execution Environment Model
Isolate JavaScript Backend: Each execution spawns a fresh Cloudflare Dynamic Worker (V8 isolate). The runtime is created per execution and destroyed immediately after the event stream terminates. No process state persists between invocations.
Container Backend: Executes inside a full Linux container that remains alive for the lifetime of the Durable Object. The container behaves like a standard Node.js process with persistent execution state.
This difference is implemented in packages/computer/src/backends/worker-javascript/worker-javascript.ts for the isolate backend and packages/computer/src/backends/container/index.ts for the container backend.
Concurrency and Resource Limits
Concurrent Execution Caps
The Isolate backend enforces a hard limit of 24 concurrent executions by default. Per the source code in worker-javascript.ts, attempting to start additional executions returns an EEXEC_BUSY error code. This protects the host from resource exhaustion but creates backpressure for high-throughput workloads.
The Container backend imposes no per-execution concurrency cap. Container-level resource limits apply, but multiple processes and threads can spawn freely within those bounds.
CPU and Timeout Constraints
| Limit Type | Isolate Backend | Container Backend |
|---|---|---|
| CPU quota | Fixed per-isolate allocation | Bound by container resources |
| Wall-clock timeout | Enforced via maxHostCallMs |
Configurable, supports long-running processes |
| Execution lifetime | Terminates with event stream | Persists across Durable Object idle periods |
The isolate's strict timeout enforcement prevents runaway computations but makes the backend unsuitable for batch processing or sustained workloads.
Network and Filesystem Access Restrictions
Outbound Networking
By default, the Isolate backend sets globalOutbound to null, blocking all outbound network calls. Selective enablement requires explicit configuration:
allowGitNetwork: Permits Git protocol operationsallowArtifactNetwork: Permits artifact registry access
The Container backend provides full outbound networking subject to standard Cloudflare Container egress policies, requiring no special flags for HTTP, DNS, or other protocols.
Filesystem Isolation
The Isolate backend routes all file I/O through the Workspace virtual filesystem (node:fs shim). Direct host filesystem access is impossible. Path confinement checks in worker-javascript.ts ensure all operations remain within the Workspace root.
The Container backend mounts the Workspace VFS while retaining full container filesystem access, including /tmp, /usr, and other standard paths. This enables temporary file usage, package installation, and interaction with system directories.
Native Code and Module Support
Binary Execution
The Isolate backend cannot load native Node.js addons or execute system binaries. Only pure JavaScript/TypeScript modules run in the V8 isolate.
The Container backend executes native binaries directly and loads compiled addons. The source code demonstrates this with modules like fuse-native that require native bindings.
Module Import Constraints
Isolate backend limitations (from docs/17_isolate_javascript.md):
- Bare imports must be pre-registered at backend construction time
- Dynamic imports require static string literals
- No support for runtime
require()resolution
Container backend capabilities:
- Standard npm module resolution
- Dynamic
require()andimport()without pre-registration - Full Node.js module compatibility
Environment and Security Model
Environment Variable Exposure
The Isolate backend exposes only the env object passed to runtime.exec() via process.env. The Durable Object's own environment is never merged, preventing secret leakage between executions.
The Container backend inherits the complete Durable Object environment, including secrets and configuration variables defined at deployment.
Sandbox Strength Trade-offs
| Security Aspect | Isolate Backend | Container Backend |
|---|---|---|
| Fresh sandbox per execution | Yes | No (container persists) |
| Host binding access | Blocked | Available via container interfaces |
| Import restrictions | Strict whitelist | Standard Node.js resolution |
| Execution isolation | Process-level | Container-level |
| Attack surface | Minimal | Larger shared runtime |
The Isolate backend's strong confinement—fresh sandbox, limited imports, strict path checks—prioritizes security over flexibility. The Container backend's weaker sandboxing accepts greater risk in exchange for operational convenience.
State Persistence and Lifecycle
Execution State Durability
In the Isolate backend, execution state exists only while the isolate runs. The documentation in docs/17_isolate_javascript.md notes that once evicted, "any un-drained output is lost." Clients must call handle.result() or maintain the stream to capture results.
Container backend processes continue execution after the Durable Object becomes idle because the container remains alive until explicitly stopped. This supports background processing and deferred result retrieval.
Capability Call Overhead
Every capability call in the Isolate backend (e.g., ws:git, ws:artifacts) traverses the isolate boundary with:
- Additional validation layers
- Byte-limit enforcement
- Cancellation handling logic
Container backend calls execute directly from the container process, bypassing isolation-layer overhead while maintaining RPC contract compliance.
Practical Selection Guide
Choose the Isolate JavaScript backend when:
- Running untrusted or third-party code
- Executing short-lived, pure-JavaScript functions
- Minimizing attack surface is paramount
- Network and filesystem access can be tightly constrained
Choose the Container backend when:
- Native binaries or compiled modules are required
- Long-running processes or background tasks are needed
- Full network and filesystem flexibility is essential
- Concurrency demands exceed 24 simultaneous executions
Code Examples
Isolate JavaScript Backend Configuration
import { Workspace } from "@cloudflare/computer";
import { WorkerJavaScriptBackend } from "@cloudflare/computer/backends/worker-javascript";
const ws = new Workspace({
storage: ctx.storage,
backends: [
new WorkerJavaScriptBackend({
loader: env.LOADER,
root: "/workspace",
access: "read-write",
maxTimeoutMs: 180_000,
modules: {
"math-kit": `export const double = v => v * 2;`,
},
}),
],
});
const handle = await ws.runtime.exec(
`
import { double } from "math-kit";
export default async function main(input) {
return { result: double(input.value) };
}
`,
{ backend: "worker-javascript", input: { value: 7 } }
);
const { result } = await handle.result();
Container Backend Configuration
import { Workspace } from "@cloudflare/computer";
import { CloudflareContainerBackend } from "@cloudflare/computer/backends/container";
const ws = new Workspace({
storage: ctx.storage,
backends: [
new CloudflareContainerBackend({
root: "/workspace",
allowGitNetwork: true,
}),
],
});
const handle = await ws.runtime.exec(
`
import { execSync } from "node:child_process";
export default async function main() {
const out = execSync("uname -a", { encoding: "utf8" });
return { sysInfo: out };
}
`,
{ backend: "container", input: {} }
);
const { sysInfo } = await handle.result();
Summary
- Concurrency: Isolate backend caps at 24 concurrent executions; Container backend has no hard per-execution limit
- Native code: Isolate backend prohibits native binaries and Node addons; Container backend supports both
- Network access: Isolate backend blocks outbound traffic by default; Container backend provides full networking
- Filesystem: Isolate backend uses Workspace VFS exclusively; Container backend mounts VFS plus container filesystem
- State persistence: Isolate backend loses state on eviction; Container backend maintains processes across idle periods
- Security model: Isolate backend offers stronger sandboxing; Container backend trades isolation for flexibility
Frequently Asked Questions
What happens when Isolate backend concurrency exceeds 24 executions?
The backend returns an EEXEC_BUSY error and rejects the new execution attempt. Implement queue-based request buffering or migrate to the Container backend for higher throughput requirements.
Can the Isolate backend access environment variables defined in the Durable Object?
No. The Isolate backend exposes only the env object explicitly passed to runtime.exec(). This intentional isolation prevents secrets from leaking between executions in multi-tenant scenarios.
Is it possible to run shell commands in the Isolate JavaScript backend?
No. The node:child_process module and system binary execution are unavailable. All functionality must be implemented in pure JavaScript/TypeScript or delegated to capability calls like ws:git.
Why does the Container backend require allowGitNetwork when the Isolate backend has the same flag?
The flag naming creates consistency, but the underlying implementation differs. In the Isolate backend, allowGitNetwork explicitly punches through the null default for globalOutbound. In the Container backend, it configures egress policy without overcoming a fundamental network block.
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 →