The argument about whether to isolate agent-generated code is finished. OWASP's Top 10 for Agentic Applications, published in December 2025 with input from more than 100 practitioners, gives unexpected code execution its own numbered category, ASI05, because an agent that can write code can be talked into running it.1 The same premise now runs through vendor documentation. Vercel titles its sandbox documentation "run agent-generated code in isolation" and frames the product around executing untrusted code without exposing production systems.2
So the isolation boundary itself is no longer the hard decision. MicroVM-per-execution went from exotic infrastructure to a one-API-call default. Vercel Sandbox puts each sandbox in its own Firecracker microVM with a dedicated kernel, and E2B's documentation opens by describing a sandbox as a fast, secure Linux VM created on demand for your agent.23 What changed is where the risk moved. Three things now decide whether your sandbox protects anything at all: what the boundary covers, what can leave through it, and what survives it. I have watched teams get all three wrong while shipping a textbook-correct Firecracker setup.
Four isolation primitives and what each one buys
Containers are the level everyone starts at, and they are the level most teams should leave. Docker and runc share the host kernel and separate workloads with Linux namespaces and cgroups. That is a policy layer, not a boundary. A kernel vulnerability in one container is a single step from host access and lateral movement to every other container on that host, which is why container escapes keep showing up as a recurring CVE genre rather than a curiosity.
gVisor takes a different route. It is an application kernel written in Go that runs in userspace, and it intercepts system calls before they reach the host kernel. Google is explicit about what it is not: not a syscall filter like seccomp-bpf, not a wrapper over Linux isolation primitives, and not a virtual machine in the everyday sense.4 The Sentry process services syscalls inside the sandbox, and filesystem access routes through a separate Gofer process over 9P. You deploy it as an OCI runtime called runsc, so Docker and Kubernetes keep working. The tradeoff is real: gVisor does not implement every system call, /proc file, or /sys file, and per-syscall overhead runs higher than executing on the host kernel.4
Firecracker is the option most agent platforms settled on. It is a minimal virtual machine monitor written in Rust that uses KVM to boot microVMs, each with its own guest kernel. AWS open-sourced it in 2018 and it backs Lambda and Fargate in production. The numbers that matter for agent workloads are in the project's own design document: a microVM configured with a minimal kernel, one core, and 128 MiB of RAM supports a steady creation rate of 5 microVMs per host core per second, so a 36-core host can create roughly 180 per second.5 Firecracker wraps the KVM boundary with two more layers. A per-microVM process runs seccomp-bpf syscall filters applied per thread, so the vCPU threads are permitted even less than the API thread, and in production the VMM launches through a separate jailer binary that sets up cgroups and chroot, drops privileges, and execs the VMM as an unprivileged process.5 Reaching the host means defeating hardware virtualization, the syscall filter, and the jailer independently.
WebAssembly is the fourth primitive and the one teams underrate for narrow work. It grants capabilities explicitly rather than isolating everything and subtracting, so an untrusted module has no filesystem or socket handle unless you hand it one. Startup is measured in microseconds and memory overhead in megabytes. The catch is compatibility: your agent's Python is not going to run in a wasm runtime without work, which is exactly why the industry default for general agent code landed on microVMs instead.

The decision rule that falls out of this is not "always use the strongest primitive." Match the primitive to the trust level of the code. Code your own engineers wrote and CI already ran can sit in a container. Code an LLM generated at runtime and nobody reviewed needs gVisor or a microVM. Code from a third party who may want to hurt you, meaning user uploads and unvetted binaries, needs the microVM and everything around it. Using a microVM for internal tooling is not wrong, it is just paying for isolation you did not need. The primitive choice answers only half the question, because it decides how hard it is to break out and says nothing about what the agent was already tricked into doing before any code ran, the tool-layer threat model we worked through in agent tool attack surface.
The scope gap: what your sandbox does not cover
Here is the failure that gets teams. They enable a sandbox, see the word in the config, and conclude the agent is contained. The containment is usually narrower than the surface.
Claude Code is the cleanest public example because Anthropic documented the limits honestly. The built-in sandbox constrains Bash commands and their child processes. The Read, Edit, and Write tools do not run through it. WebFetch does not run through it. MCP servers and hooks are separate processes that run unconstrained on the host.6 So "the sandbox is on" means shell commands are contained, not that every file operation the agent performs is contained. The gap has a sharp edge: Claude Code recognizes common file commands inside Bash, like cat, sed, and grep, and applies your Read and Edit deny rules to them. A Python one-liner that opens ~/.ssh/id_rsa is not recognized as a file read, so it walks past those rules unless the OS sandbox is enforcing filesystem policy underneath.
The implementation details matter if you operate any of this. On macOS the sandbox uses Seatbelt, which ships with the OS, so there is nothing to install. On Linux and WSL2 it needs bubblewrap for filesystem isolation and socat to relay traffic through the sandbox proxy. An optional seccomp filter adds Unix domain socket blocking and installs from @anthropic-ai/sandbox-runtime.6 WSL1 is unsupported, because bubblewrap needs kernel features only WSL2 exposes. On Ubuntu 24.04 and later, the default AppArmor policy blocks bubblewrap from creating the user namespaces it needs, and you have to check kernel.apparmor_restrict_unprivileged_userns and add a profile for bwrap when it returns 1.6 None of that is exotic, but all of it is invisible until a command silently stops being sandboxed.
That silent failure is the one to design against. By default, if the sandbox cannot start because a dependency is missing or the platform is unsupported, Claude Code shows a warning and runs commands anyway. Sandboxing fails open. Setting sandbox.failIfUnavailable to true turns that into a hard failure, which is what a managed deployment should do if sandboxing is a security gate rather than a convenience.6
The escape hatch is worth understanding rather than fearing. When a command cannot run sandboxed, Claude Code reports the violation and the model may retry with the dangerouslyDisableSandbox parameter. That retry does not bypass your controls; it drops back into the normal permission flow, a confirmation prompt in Manual mode or the classifier in auto mode. If you want the door bricked up rather than doorbelled, allowUnsandboxedCommands: false turns on Strict sandbox mode, and the tool then ignores the parameter unless the command is on your excludedCommands list.6
The lesson generalizes past one vendor. Ask what fraction of the agent's action surface sits inside the boundary, and write the answer down. Anthropic's own environment comparison gives you the ladder to climb: the sandboxed Bash tool covers shell commands only; the sandbox runtime wraps the whole Claude Code process including file tools, MCP servers, and hooks; a dev container or custom container covers the full development environment; a virtual machine covers a full operating system.7 The higher rungs cost setup effort and give you a boundary that matches the phrase "the agent is sandboxed." Any run under --dangerously-skip-permissions or an unattended auto-mode session needs a container, a VM, or the sandbox runtime, not just the Bash sandbox, because the permission prompts you removed were the layer catching the rest.7 Choosing that rung is the same exercise as setting a blast radius for autonomy, which we covered in governing agent autonomy, except the boundary is enforced by the operating system instead of by a policy document.

Egress is the boundary everyone forgets
A sandbox that stops code from escaping the host still lets that code talk to the internet, and the internet is enough to lose. Firecracker's design document is unusually blunt: the project performs no network traffic filtering, treats all egress from a guest as untrusted, and expects it to be filtered at the host level.5 The isolation primitive protects the host. It does not protect your data.
Managed platforms make the same point through their defaults. Vercel's sandbox documentation states that a new sandbox has network access for installing packages and making API calls, and that sandboxes have "controlled outbound access" through their own network namespace.8 Controlled by whom, by default, is the question to ask in writing. If your orchestration never narrows the policy before untrusted code runs, the microVM still protects the host while the sandbox sends out anything you placed inside it.
Claude Code's network model shows what a tight policy looks like in practice, and where the sharp edges are. No domains are pre-allowed. The first time a command needs a new host, the tool prompts, and approving keeps that host for the rest of the session. Traffic routes through a proxy that checks the allowlist, with a Seatbelt backstop blocking non-loopback traffic for clients that ignore proxy environment variables.6 Two configuration keys deserve a second look before you paste them into a shared settings file. Allowing broad domains like github.com creates a data exfiltration path, with domain fronting named explicitly in the documentation. Allowing Unix sockets can grant host access in ways that are easy to miss, because allowing /var/run/docker.sock effectively hands over the host system, and allowAppleEvents on macOS removes code execution isolation.6
Then there are credentials, which are the real prize. Vercel's documentation makes the point directly: everything you place in the sandbox environment is readable from inside it.8 The pattern that holds up is to give the sandbox nothing durable. Pass a narrowly scoped, short-lived token as a runtime argument at the moment of the call rather than a long-lived environment variable that survives the whole session. That is the same fix as the broader problem we called out in agent identity: an agent should hold a credential that belongs to a specific task, with a specific scope and a specific expiry, not a borrowed service account that outlives the work. Vercel's OIDC authentication exists for the same reason: Vercel issues a token bound to your project rather than leaving a long-lived access token in configuration, and in production on Vercel the authentication is automatic.2

State is the new attack surface
Ephemerality used to carry security value for free. A compromised sandbox died with its session. That property is being traded away across the industry, mostly for good reasons.
Vercel made sandboxes persistent by default. When a sandbox stops, the SDK automatically snapshots the filesystem and restores it the next time you resume, including installed packages and the working tree, and the sandbox configuration is preserved across sessions.8 Wipe-on-stop is now the opt-out, not the design. E2B caps sessions at an hour on the free tier and 24 hours on the paid tier, which only makes sense if sessions are expected to live long enough to matter. E2B's free tier includes a one-time $100 of usage credits, up to 1-hour sessions, and 20 concurrent sandboxes; Pro runs $150 per month with 24-hour sessions and 100 concurrent sandboxes, purchasable up to 1,100.9
The reason is obvious once you have run a coding agent. A one-shot "solve this in Python" task is genuinely ephemeral. An agent that clones a repository, installs dependencies, and iterates for twenty minutes cannot re-run npm install on a blank VM every turn. State is the product.
The security consequence is less obvious. A persistent workspace that gets poisoned resumes poisoned. A malicious postinstall script or a tampered .bashrc survives the stop and comes back on the next resume, which moves the trust question from "is this execution safe" to "is this workspace's history safe." That is a different question with a different answer, and it is the same one we ran into building durable execution for agent runs: the moment a system can resume, every piece of state it resumes from becomes an input you have to trust. If you are running genuinely untrusted third-party code rather than your own model's output, opt back out of persistence, eat the setup cost, or pin sandboxes to snapshots you control and treat drift from the snapshot as a signal.
The same reasoning applies to running multiple agents inside one microVM, which the platforms now support and which looks like isolation without being it. Vercel lets you create a Linux user per agent with a private home directory, plus groups to open a shared workspace when agents need to collaborate.10 The documentation describes the boundary accurately: one agent running as its own user cannot read, list, or write another agent's home. What that buys is protection from accidental cross-talk among cooperative agents. It is Unix file permissions, not a kernel, and the same API exposes sudo: true for a single privileged command and a root handle for a persistent one.10 If your agents are mutually untrusted, whether they are separate customers or adversarial roles, one microVM gives one boundary. You need a microVM per tenant, not a UID per tenant. That distinction matters most for the failures that spread, which is the pattern we documented in multi-agent failure modes: a boundary that isolates one agent's damage is what keeps a single bad step from becoming a fleet-wide incident.

The five questions and the Kubernetes-native path
The audit that prevents most of this is short enough to run in a design review. First, what exactly is inside the boundary, component by component? Second, what is the default egress policy, and who narrows it before untrusted code runs? Third, which credentials can the sandbox reach, and how long do they live? Fourth, is state persistent, and who could poison it? Fifth, where does the orchestrator live relative to compute?
That last question is the one that keeps showing up in incident reviews. Keep authentication, billing, audit logging, human review, and recovery state outside any single sandbox. The compute plane gets narrow credentials, a narrow filesystem, and nothing else. If the orchestrator shares a kernel with the code it orchestrates, the sandbox is decorative.
If you already run Kubernetes, you no longer need to assemble this from scratch. The kubernetes-sigs/agent-sandbox project, under the umbrella of SIG Apps, defines a Sandbox custom resource for isolated, stateful, singleton workloads, which is the shape an agent runtime actually takes: one long-running environment with stable identity and persistent storage, rather than a replicated Deployment.11 It deliberately does not implement isolation itself. It delegates to secure runtimes like gVisor or Kata Containers through Kubernetes RuntimeClass, and exposes warm-pool primitives (SandboxTemplate, SandboxClaim, SandboxWarmPool) so you can pre-warm sandboxes instead of paying cold start per request.11 That warm-pool design is what makes high-throughput agent evaluation loops practical, and it is the same reason the project lists reinforcement learning and SWE-bench style harnesses as primary use cases.11
Cost should not be the reason you skip isolation. E2B's per-second pricing for a 2 vCPU sandbox works out to about $0.000028 per second, roughly $0.10 per hour, billed per second of a running sandbox.9 Vercel bills active CPU at $0.128 per vCPU-hour and provisioned memory at $0.0212 per GB-hour, and time spent waiting on I/O, including waiting on a model call, does not count toward active CPU. Hobby accounts get 5 CPU-hours, 420 GB-hours of memory, 5,000 sandbox creations, and 20 GB of data transfer per month at no cost; Pro sessions run up to 24 hours with up to 10,000 concurrent sandboxes, and sandboxes run in 19 regions including Washington D.C., San Francisco, Cleveland, and Paris.12 The isolation tax is measured in milliseconds and fractions of a cent, not in seconds and dollars.

The isolation question is closed. A microVM per execution is available as a default and costs almost nothing. The operational question is not closed, and it is the one that decides whether you are running agent code inside a boundary or inside a boundary-shaped configuration. Audit the scope, the egress, and the state, and you will find at least one of the three open in any agent system you have shipped.
Sources
-
OWASP GenAI Security Project, "OWASP Top 10 for Agentic Applications for 2026," published December 9, 2025, developed with more than 100 industry contributors. genai.owasp.org ↩
-
Vercel, "Run agent-generated code in isolation," Vercel Sandbox documentation covering the Firecracker microVM model, agent workflows, and OIDC authentication. vercel.com ↩ ↩2 ↩3
-
E2B, "E2B Documentation," describing a sandbox as a fast, secure Linux VM created on demand for your agent, with templates defining the starting environment. docs.e2b.dev ↩
-
gVisor project, "What is gVisor?," covering the userspace application kernel, the Sentry and Gofer split over 9P, the
runscOCI runtime, the explicit distinction from syscall filters and virtual machines, and the documented compatibility and per-syscall overhead tradeoffs. gvisor.dev ↩ ↩2 -
Firecracker project, "Firecracker Design," official design document covering the microVM creation rate, per-thread seccomp-bpf filters, the jailer privilege drop, and the statement that Firecracker performs no network traffic filtering. github.com ↩ ↩2 ↩3
-
Anthropic, "Configure the sandboxed Bash tool," Claude Code documentation covering Seatbelt and bubblewrap enforcement, the optional seccomp filter and
sandbox-runtimepackage,sandbox.failIfUnavailable,allowUnsandboxedCommands, thedangerouslyDisableSandboxretry behavior, the AppArmor restriction on Ubuntu 24.04, and the named network, Unix-socket, and Apple Events limitations. code.claude.com ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 -
Anthropic, "Choose a sandbox environment," Claude Code documentation comparing the sandboxed Bash tool, sandbox runtime, dev container, custom container, and virtual machine approaches, including the guidance that
--dangerously-skip-permissionsruns belong inside a container, VM, or the sandbox runtime. code.claude.com ↩ ↩2 -
Vercel, "Understanding Sandboxes," Vercel Sandbox documentation covering the Firecracker microVM model, dedicated kernel per sandbox, environment readability, controlled outbound access, and persistent-by-default filesystem snapshots. vercel.com ↩ ↩2 ↩3
-
E2B, "Pricing," covering per-second sandbox pricing, session-length limits, and concurrency limits across the free and Pro tiers. e2b.dev ↩ ↩2
-
Vercel, "Run isolated AI agents in one sandbox," Vercel Sandbox documentation covering per-agent Linux users, private home directories, shared groups, and the
sudooption. vercel.com ↩ ↩2 -
Kubernetes SIG Apps,
agent-sandbox, covering theSandboxcustom resource, delegation of isolation to sandbox runtimes viaRuntimeClass, the warm-pool extensions, and the agent runtime and reinforcement learning use cases. github.com ↩ ↩2 ↩3 -
Vercel, "Vercel Sandbox pricing and quotas," covering active-CPU and provisioned-memory rates, Hobby allotments, session duration limits, concurrency, and available regions. vercel.com ↩



