Your coding agent just ran pip install from a typosquatted package, and the post-install hook is scanning the network. The only question that matters now is what sits between that sandbox and the paying tenant's pod on the same node: a userspace kernel, a lightweight VM, or a microVM with a hardware boundary. This post compares gVisor, Kata Containers, and Firecracker on the three numbers that decide an agent execution layer — cold-start latency, memory overhead per sandbox, and blast-radius containment — and ends with a decision rule for a self-hosted fleet. Here is the verdict table up front; the rest of the post earns it.
| gVisor (runsc) | Kata Containers | Firecracker | |
|---|---|---|---|
| Isolation boundary | Userspace kernel (Sentry) + seccomp; host kernel still shared | Full guest kernel in a lightweight VM per pod | KVM microVM, dedicated kernel, ~50k-line Rust VMM |
| Cold start | ~200–600ms for a real sandbox create | ~500ms–1s on QEMU; ~125–625ms on Firecracker backend | ~125ms boot; tens of ms from snapshot |
| Memory overhead per sandbox | ~18–50MB (Sentry process) | ~50–130MB on QEMU; ~5–30MB on Firecracker backend | ~5MB per microVM |
| Kubernetes fit | Native: RuntimeClass: gvisor | Native: pod semantics, your choice of hypervisor | DIY: JSON API + vsock; you build the orchestrator |
| Escape must break | Sentry, then the shared host kernel | Guest kernel, then the hypervisor | Guest kernel, then KVM + a minimal VMM |
| Proved at | GKE Sandbox, Modal | AKS/OpenShift confidential containers | AWS Lambda, E2B, Fly.io |
The threat model: untrusted code on shared nodes
An agent execution layer is not a CI runner with nicer branding. The workload is model-generated code — arbitrary shell, dependency installs from public registries, outbound network to fetch repos and docs — running on nodes that also host tenant apps. Prompt injection, typosquatted packages, and malicious build scripts are not edge cases; they are the expected input. "Blast radius" here has a precise meaning: if one sandbox is fully compromised (root inside the sandbox, hostile code running), what does the attacker see next, and is a neighboring tenant's pod reachable without a second exploit?
That framing is what makes this comparison different from a generic container-security rundown. All three runtimes beat plain runc containers against container escape. The question is what the second barrier is made of, and what it costs you in start latency and memory on every sandbox you run.
One more scoping note: none of these three solves the network half. Egress filtering, per-tenant traffic attribution, and CNI policy still sit outside the runtime choice. Isolation runtimes contain the blast; they do not watch the exits.
gVisor: a userspace kernel between the agent and your nodes
gVisor takes the approach of never letting the sandbox touch the host kernel directly. Application syscalls are intercepted by the Sentry — a userspace process that implements the Linux ABI itself — while a second process, the Gofer, mediates filesystem access from the host side. The Sentry runs unprivileged and heavily seccomp-filtered, so even a full compromise of the sandboxed application lands the attacker in a userspace process with a tiny allowed-syscall surface, not in kernel context.
The operational superpower is how little changes to adopt it. gVisor ships as an OCI runtime (runsc) behind a Kubernetes RuntimeClass, so a pod opts in with one field and keeps normal pod semantics: same images, same volumes, same kubectl workflow. Google runs it as GKE Sandbox, and Modal — the serverless GPU platform reportedly valued around $1.1B — runs customer code under gVisor at scale. That is serious production evidence for the "run untrusted code next to other tenants" shape.
The costs are twofold. First, every syscall the workload makes is emulated or proxied, which shows up as overhead on syscall-heavy work: published figures range from modest single-digit slowdowns on compute-bound code to dramatically slower filesystem and network-path benchmarks, with the KVM platform and directfs work narrowing the gap over the years. An agent compiling code and streaming tool output is closer to the syscall-heavy end than a web server is. Second, the host kernel is still shared. gVisor shrinks the reachable syscall surface enormously but does not add a hardware boundary — a kernel vulnerability reachable through the Sentry's allowed set is, in principle, one exploit away from every tenant on the node.
In practice, teams report real sandbox creates around a few hundred milliseconds and roughly 18–50MB of Sentry overhead per sandbox. That makes gVisor the density-friendly, adopt-it-this-week option — and the one whose worst case you must be honest about.
Kata Containers: a VM per pod, with your choice of hypervisor
Kata Containers answers the shared-kernel problem by giving each pod its own guest kernel inside a lightweight virtual machine. The pod keeps Kubernetes-native semantics — it is still a pod to the scheduler, the CNI, and kubectl — but processes inside it run under a kernel that no other tenant shares. An escape now requires breaking the guest kernel and the hypervisor, which is a meaningfully taller ladder than one kernel exploit.
Kata's distinctive trait is that the hypervisor is pluggable: QEMU, Cloud Hypervisor, Firecracker, and Dragonball are all supported backends, and the choice changes the numbers more than anything else about Kata. On QEMU, expect cold starts around half a second to a second and guest overhead in the ~50–130MB range — the heaviest footprint of the three options here, buying the most mature device model. On the Firecracker backend, boot drops toward ~125ms and overhead toward tens of megabytes — but with a real caveat operators trip over: Firecracker has no native virtio-fs or 9p support, so Kata-on-Firecracker needs the nydus snapshotter (plus a userspace nydusd daemon) to deliver the root filesystem into the microVM. That is an extra moving part in the storage path that Kata-on-QEMU simply does not have, since QEMU reads the host's overlayfs rootfs directly over virtio-fs.
Kata is also the runtime with the clearest confidential-computing story: it is the foundation of Confidential Containers (CoCo), which runs the Kata agent inside a TDX or SEV-SNP confidential guest for hardware-encrypted pod memory plus attestation. If your roadmap has a "regulated tenants who ask about memory encryption" chapter, Kata is the only one of the three that already lives in that world. Note the asymmetry this creates: Kata's QEMU and Cloud Hypervisor backends can target TEEs, while Firecracker itself ships no confidential-guest support — worth knowing before you standardize on Kata-with-Firecracker and assume the CoCo story comes along.
Firecracker: the microVM the agent-cloud industry converged on
Firecracker is the minimalist: a virtual machine monitor written in roughly 50,000 lines of Rust (against QEMU's ~1.4 million lines of C), exposing a deliberately tiny device model — virtio net, block, and vsock, essentially nothing else — with a built-in jailer that sandboxes the VMM process itself. AWS built it for Lambda and Fargate, where the economics demand booting thousands of isolated execution environments per second per host.
The headline numbers are why the agent-sandbox industry standardized on it. A cold microVM boots in about 125ms with roughly 5MB of VMM overhead, and snapshot/restore collapses the start path further: boot once, snapshot the whole machine state, restore clones in tens of milliseconds. AWS's own Lambda SnapStart is this technique at planetary scale, and the agent-cloud vendors followed the same playbook.
E2B — open-source agent sandbox infrastructure, every sandbox a Firecracker microVM — grew from around 40,000 sandboxes a month in early 2024 to over 15 million a month by early 2025, raised a $21M Series A in July 2025, and reports the large majority of the Fortune 100 signed up. Fly.io runs Machines on Firecracker and extended the model to Sprites, persistent VMs for coding agents with checkpoint/restore, in October 2025. Vercel's sandboxes and Cloudflare's sandbox offering sit on the same substrate. When every vendor serving your exact workload picks the same VMM, that is not fashion — it is the shape of the cost curve.
The price of that minimalism is orchestration: Firecracker is a JSON API and a vsock channel, not a Kubernetes runtime. There is no pod semantic, no scheduler integration, no CNI plugin waiting for you. E2B's self-hosted path pairs Firecracker with Nomad on cloud VMs with nested virtualization; a bare-metal fleet owner wires it into their own control plane. You get the smallest per-sandbox footprint and the strongest default boundary — dedicated kernel, KVM hardware isolation, minimal attack surface — and you accept that the "fleet of sandboxes" layer above it is yours to build and operate.
Density math: what each runtime costs per node
Memory overhead per sandbox only matters relative to the node it sits on, so run the arithmetic for a typical self-hosted box — say a 64GB Hetzner dedicated server. At ~5MB of overhead, Firecracker lets the runtime fade into the noise: a thousand idle-warm microVMs cost about 5GB, and the binding constraint is the workload memory inside them. gVisor at ~18–50MB per Sentry means a thousand warm sandboxes reserve roughly 18–50GB before workloads run — noticeable, and the reason warm-pool sizing under gVisor is a real capacity-planning exercise. Kata on QEMU at ~50–130MB per pod VM makes large warm pools painful: a few hundred idle sandboxes can eat half the node, which pushes Kata fleets toward cold-boot-per-task or toward the Firecracker backend with its nydus tradeoff.
Be honest about what dominates, though. An agent sandbox carrying a language toolchain, a repo checkout, and model-context scratch space easily wants 512MB–1GB of workload RSS — ten to a hundred times any of these runtime overheads. Runtime footprint decides how cheap your idle warm pool is and how many concurrent sandboxes fit at the margin; workload memory decides the headline density. Anyone selling you an isolation runtime purely on megabytes-per-sandbox without showing the workload line is cherry-picking the comparison. The table that matters has both rows, and on the workload row all three runtimes tie.
Latency has the same shape. Cold-boot gaps (125ms vs 500ms vs a second) look dramatic in a benchmark table and nearly vanish inside an agent loop where each step waits seconds on model inference. Where start latency genuinely bites is interactive use — a developer waiting on a sandbox-backed terminal — and burst scale-out, where a thousand cold starts serialize into minutes of pool refill. Snapshot/restore is Firecracker's answer to both, and it is telling that the warm-pool pattern (pre-booted sandboxes claimed in milliseconds) shows up in every vendor's architecture regardless of runtime: pools exist precisely because nobody wants to pay cold start on the critical path.
Blast-radius walkthrough: one compromised sandbox, three outcomes
Now run the scenario from the top of the post — hostile code has root inside the sandbox — under each runtime, with a tenant pod on the same node.
Under gVisor, the attacker is root in a container whose syscalls are served by the Sentry. To reach the tenant, they must first escape the Sentry sandbox (a userspace process under seccomp with a minimal allowed-syscall set), landing in host userspace — and then exploit the host kernel, which the tenant pod shares. Two exploits, but the second one targets a kernel that also runs your paying workloads. This is the residual risk the gVisor choice carries: defense in depth, but the last line is still shared.
Under Kata on QEMU, the attacker is root in a guest kernel nobody else uses. Reaching the tenant means a guest-kernel exploit plus a QEMU escape — and QEMU's ~1.4M-line device-model surface is exactly why hypervisor CVEs get taken seriously. The tenant's kernel was never in the blast radius at all. The boundary is a hardware line, paid for in memory and boot time.
Under Firecracker, the ladder is the same shape — guest kernel, then the VMM — but the second rung is far narrower: ~50,000 lines of memory-safe Rust, five-ish emulated devices, the jailer boxing the VMM process itself. A hypervisor escape here has dramatically less code to hide in. That is the containment argument behind the industry's convergence: not that Firecracker is magic, but that its second barrier has the smallest surface of the three by an order of magnitude.
One compromised sandbox, then: gVisor contains it in userspace with a shared kernel behind that; Kata contains it in a VM with a large hypervisor behind that; Firecracker contains it in a VM with a minimal hypervisor behind that. Pick the sentence you can defend to your tenants.
The decision rule
For a self-hosted agent execution layer sharing nodes with tenants, the choice reduces to three situations:
Ship the agent layer this month: gVisor. One RuntimeClass, pod semantics unchanged, density close to plain containers, and Modal-scale proof that it holds up under untrusted code. Accept the shared kernel explicitly — track kernel CVEs against your node image like a first-class dependency, and keep egress filtering tight since a contained-but-chatty sandbox is still a spam source.
Maximize containment per sandbox with Kubernetes semantics: Kata on QEMU. A real VM boundary per pod, the only path of the three toward confidential computing for regulated tenants, at the cost of the heaviest footprint and slowest cold starts. Budget warm-pool memory honestly using the density math above, and only reach for the Firecracker backend if you have verified the nydus rootfs path in your storage stack first.
Maximize density of untrusted execution and own the control plane: Firecracker direct. The smallest footprint, the fastest restore-from-snapshot path, the minimal escape surface — and a DIY orchestrator to build. This is the E2B-shaped bet: justified when sandbox count is your scaling axis and you have the engineering capacity to operate a bespoke sandbox fleet rather than borrowing Kubernetes' scheduler.
Whichever you pick, revisit the decision when the workload mix moves. A fleet that starts with trusted internal agents debugging deploys and grows into running arbitrary tenant-submitted agent code has crossed the line where the shared-kernel residual risk stops being theoretical. The isolation choice is not permanent — runtimes can mix per workload class behind different RuntimeClasses or pools — but it should always be deliberate, written down, and re-audited when the threat model changes. Your tenants cannot see the boundary. That is exactly why you owe them a good one.
Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own. If your agents are going to deploy and operate apps, they deserve a platform built with agent-shaped workloads in mind. Star the repo on GitHub or deploy your first app today.



