Secure Exec vs Container Sandbox
Secure Exec and container sandboxes both run untrusted code in isolation, but they sit at different points on the weight-versus-flexibility curve. Picking the right one depends on what you need to run.
Secure Exec boots a fully virtualized VM for each runtime and runs guest JavaScript in a V8 isolate inside a dedicated sidecar process. There is no Docker daemon, no orchestrator, and no vendor account: you npm install the package and call NodeRuntime.create(). It is built for lightweight, high-fanout code execution like AI tool calls, user scripts, and plugins, where you want granular permissions and a small footprint.
Container sandboxes (e2b, Daytona, Modal, Cloudflare Containers, and similar) spin up a full OS image with system packages, a writable disk, and the ability to run arbitrary binaries. They are built for heavyweight workloads that need a complete environment: coding agents, long-lived dev sessions, or anything that shells out to native tools.
How isolation works
Section titled “How isolation works”The biggest difference is what the isolation boundary is made of.
- Secure Exec runs guest code in a V8 isolate hosted by its own sidecar process. Every
NodeRuntime.create()is a separate VM with its own virtual filesystem, process table, network policy, and crash domain. Guest code never calls real Node.js builtins, never opens a real host socket, and never touches the real disk. Every syscall is routed through the kernel. See Process Isolation. - Container sandboxes isolate with OS-level primitives (namespaces, cgroups, or a microVM). The guest runs a real kernel and a real filesystem, so it can do anything a Linux process can, constrained by the container’s configuration.
Secure Exec presents normal Linux semantics to the code it runs (a POSIX-like virtual filesystem, processes, pipes, PTYs, sockets) without granting access to the host that backs them.
Comparison
Section titled “Comparison”| Dimension | Secure Exec | Container Sandbox |
|---|---|---|
| Isolation boundary | Virtualized VM + V8 isolate in a sidecar process | OS container or microVM |
| Setup | npm install secure-exec | Vendor account or Docker host |
| Hardware | Runs on your infrastructure | Often vendor-hosted |
| Filesystem | Virtual, in-memory, per runtime | Full OS filesystem |
| Network | Denied by default, opt-in per runtime | Full, or firewall rules |
| Permissions | Per-scope policy (fs, network, childProcess, process, env, tool) | Coarse, container-level |
| Languages | JavaScript and TypeScript (Node-compatible) | Any language the image supports |
| Arbitrary binaries | No (guest binaries run through the VM, not the host) | Yes |
| Crash domain | One process per runtime | One container per sandbox |
What Secure Exec gives you
Section titled “What Secure Exec gives you”These capabilities are the reason to reach for Secure Exec over a container when the workload fits in a Node-compatible runtime:
- Deny-by-default permissions. Network egress is blocked until you opt in, and every guest syscall is checked against a per-scope policy before any host resource is touched. A denied operation fails with
EACCES. See Permissions. - Virtual filesystem. Each runtime gets its own in-memory filesystem. Guest reads and writes never reach the host disk, and two runtimes writing the same path do not collide. See Filesystem.
- Mediated networking. Guest
fetch(),node:http, and raw sockets all flow through the kernel socket table, so you can allow, deny, or rule-match outbound traffic. See Networking. - Process-level isolation. Each runtime is its own VM and its own crash domain, and each
exec()/run()starts a fresh guest process so in-memory state never leaks between runs. See Process Isolation. - npm compatibility. Real npm packages run unmodified inside the VM, resolved over a faithfully mounted
node_moduleslike a real filesystem. See Module Loading.
When to use each
Section titled “When to use each”Use Secure Exec when
Section titled “Use Secure Exec when”- You are running JavaScript or TypeScript (AI tool calls, user scripts, plugins, evaluation loops).
- You want no vendor dependency and to run on your own infrastructure.
- You need granular, deny-by-default permissions over the filesystem, network, and child processes.
- You are running many short tasks and want a small per-task footprint.
Use a container sandbox when
Section titled “Use a container sandbox when”- You need a full OS environment: system packages, arbitrary binaries, or languages beyond a Node-compatible runtime.
- You need a persistent, long-lived environment such as a multi-hour dev session.
- The workload genuinely needs a real kernel and a real disk.
A minimal example
Section titled “A minimal example”Booting a Secure Exec runtime takes no infrastructure beyond the installed package. The runtime owns its VM until you dispose it:
import { NodeRuntime } from "secure-exec";
// Network is denied by default; the guest runs in its own virtualized VM.const rt = await NodeRuntime.create();try { const result = await rt.run<number>(` __return(40 + 2); `); console.log(result.value); // 42} finally { await rt.dispose();}There is no container to build, no image to pull, and no daemon to keep running.
Performance
Section titled “Performance”Because there is no container image or microVM to boot, a Secure Exec runtime starts in a fraction of the time a container takes, and the dominant cost is bringing up the guest runtime rather than provisioning infrastructure. For workloads that run the same kind of snippet repeatedly, reusing a live guest process drives warm execution into the low-millisecond range. See Benchmarks for measured cold-start, warm, and reuse numbers and the methodology behind them.