AI Agent Sandboxes Compared: Firecracker vs gVisor vs Kata for Untrusted Code in 2026
Your agent just wrote a Python script and asked your platform to run it. Nobody reviewed that script. It may pip install a package chosen by a poisoned search result, or open a socket to an address a web page told it to open. Choosing an AI agent sandbox is therefore not a performance decision dressed up as a security one — it is the single architectural choice that decides how far a bad instant can travel. In 2026 the field has effectively narrowed to three serious engines: Firecracker microVMs, gVisor’s user-space kernel, and Kata Containers. They differ less in features than in where the isolation boundary physically sits, and that difference propagates into cold start, GPU access, egress control and your monthly bill.
What this covers: the decision drivers that actually separate the three engines, an honest options analysis with every latency figure attributed to its source, a decision and its consequences, the GPU constraint that often overrides everything else, and the anti-patterns that keep showing up in production incident reviews.
Context and Background
Until roughly 2023, “run untrusted code” was a niche problem owned by CI vendors, online judges and a handful of serverless platforms. Agentic systems made it a mainstream platform requirement. Any product that lets a model write and execute code — a data analyst agent, a coding agent, a spreadsheet-to-chart tool, an autonomous SRE — now needs a per-request execution environment that can be handed hostile input hundreds of times a minute.
The incumbents arrived from different problems. Firecracker was built by AWS for Lambda and Fargate: a minimal virtual machine monitor (VMM) written in Rust that boots a purpose-built guest kernel on KVM with almost no device emulation. gVisor came from Google’s need to run arbitrary customer containers on shared infrastructure without exposing the host kernel’s full syscall surface; it reimplements Linux in user space. Kata Containers came from the OpenStack and Intel Clear Containers lineage, and its goal was compatibility: make a normal OCI container transparently run inside a lightweight VM so Kubernetes does not have to know.
Those origins still predict their behaviour. Firecracker optimises for density and boot latency but historically refused device breadth. gVisor optimises for container-shaped compatibility and fast startup but pays a structural tax on syscall-heavy work. Kata optimises for “it is still a pod”, which is why it is the only one of the three that plugs into Kubernetes as a first-class RuntimeClass.
The 2026 industry position is fairly settled: shared-kernel containers are not considered an adequate boundary for model-generated code, and microVM-class isolation is the default recommendation for multi-tenant execution. That consensus is documented in vendor engineering write-ups and, more usefully, in a peer-reviewed comparative security study of code sandboxes that evaluates these engines on attack surface, CVE history and patch cadence rather than marketing claims (arXiv 2606.08433). If you are still deciding whether you need a boundary at all, the related question of how hostile input reaches your agent in the first place is covered in our analysis of prompt injection and agentic AI security; this post assumes you have accepted that the code is untrusted and are choosing the container for it.
Decision Drivers: Where the Isolation Boundary Actually Sits
The decisive question for an AI agent sandbox is simple: when the workload makes a system call, what code services it, and how much host kernel remains reachable afterwards? Plain containers reach the full host syscall surface. gVisor terminates most syscalls in a user-space kernel. Kata and Firecracker terminate them in a separate guest kernel, so the host sees only KVM ioctls and the VMM’s device models. Everything else in this comparison is downstream of that sentence.

Figure 1: Where the isolation boundary sits in each execution model.
Figure 1 traces the path from agent-generated code down to the host Linux kernel for four models. A plain container’s syscalls land directly on the host kernel, with only namespaces, cgroups and a seccomp profile in between — a large and constantly changing surface. A gVisor sandbox routes them into the Sentry, a Linux-compatible kernel written in Go, which services most of them itself and forwards a deliberately narrow, seccomp-filtered subset to the host. Kata and Firecracker both give the workload a real guest kernel; the host only ever sees KVM ioctls plus whatever the VMM’s virtio device models expose. The number of distinct host entry points shrinks by roughly an order of magnitude as you move down that list, and that shrinkage is the product you are buying.
Driver 1: Attack surface and the size of the trusted computing base
Kernel privilege-escalation bugs are the threat you are actually defending against. A container escape typically chains a kernel bug reachable from an ordinary syscall; the Linux syscall interface is enormous and is extended every release. Under gVisor, that same bug is usually unreachable because the Sentry never issues the vulnerable syscall on the workload’s behalf — but the Sentry itself becomes a new, smaller target, and its host-facing filter is the thing that must hold.
Under Firecracker and Kata, the workload owns a guest kernel it is welcome to compromise. Owning the guest buys nothing by itself; the attacker must then break the VMM or KVM. This is where Firecracker’s minimalism earns its keep: the project is commonly described as roughly 50,000 lines of Rust against QEMU’s roughly 1.4 million lines of C (figures widely reported in vendor and community engineering write-ups, not measured by us). Kata can run on QEMU or on Cloud Hypervisor; choosing Cloud Hypervisor narrows the VMM surface considerably, and that choice is worth making explicitly rather than inheriting a default.
The practical test is not which engine is theoretically stronger but which one has a defensible patch story. Track each project’s CVE cadence and how quickly your distribution ships fixes. A microVM stack you patch twice a year is weaker in practice than a gVisor deployment you rebuild weekly.
Driver 2: Cold start under bursty, short-lived agent traffic
Agent workloads are spiky and short. A user asks a question, three tool calls fire within a second, each wants an execution environment, and every one of them exits in under ten seconds. Sustained throughput barely matters; the p99 of “time from request to first instruction” is the whole operational story.
Firecracker’s headline figure is a microVM boot in about 125 ms, and the project also advertises a per-VM memory overhead below 5 MiB and a creation rate on the order of 150 microVMs per second per host (all from Firecracker project material — reported, not measured here). Boot time alone is not the interesting number, though. Snapshot-restore is: you boot once, let the language runtime and your libraries initialise, snapshot the memory and device state, and then resume clones of that snapshot. Firecracker’s own snapshot documentation describes restore in the low single-digit milliseconds, and a widely circulated 2026 practitioner write-up reports end-to-end sandbox readiness around 28 ms using snapshot restore (dev.to — one author’s measurement on their own hardware, not a controlled benchmark). AWS uses the same mechanism for Lambda SnapStart.
gVisor’s startup is fast for a different reason: there is no kernel to boot at all. runsc starts a process. Kata sits between the two and depends heavily on VMM choice, kernel configuration and whether you use a prebuilt rootfs image or pull a container image per pod.
Driver 3: GPU passthrough, which frequently decides the whole thing
If your agent only shells out, runs pandas and renders a chart, ignore this section. If it runs a local model, fine-tunes, or does anything CUDA-shaped inside the sandbox, GPU support will likely override every other driver.
gVisor supports GPUs through nvproxy, which proxies NVIDIA driver ioctls from the sandbox to the host driver. It is real and it works for CUDA, Vulkan and NVENC/NVDEC workloads, but the design imposes hard limits, and the project documents them plainly (gVisor GPU docs). The supported ioctl set is deliberately restricted to keep maintenance tractable, so an unimplemented ioctl surfaces as an opaque CUDA failure. Support is also pinned to an allowlist of NVIDIA driver versions — runsc nvproxy list-supported-drivers is the authority, and a driver outside that list requires the --nvproxy-allow-unsupported-driver escape hatch. Known rough edges reported in the project’s issue tracker include flaky cudaMallocManaged() on the KVM platform and failing CUDA checkpoint/restore inside the sandbox.
Kata takes the orthodox path: VFIO device passthrough. You enable the IOMMU, bind the physical GPU to vfio-pci, and the guest gets the device. NVIDIA supports this officially through the GPU Operator, which ships a nvidia-vfio-manager to perform the binding and a nvidia-cc-manager for confidential-computing mode (NVIDIA GPU Operator Kata docs). Pods then request a passthrough GPU resource such as nvidia.com/pgpu. The Kata runtime discovers allocated devices via Kubelet’s Pod Resources API; NVIDIA’s documentation notes that clusters older than Kubernetes 1.34 must enable the KubeletPodResourcesGet feature gate, which is on by default from 1.34.
Firecracker is the awkward case, and most comparisons get it wrong in one direction or the other. Historically Firecracker had no PCI bus and no VFIO device assignment at all — an explicit design decision to keep the attack surface minimal. That is why “Firecracker cannot do GPUs” became received wisdom. As of 2026 that is changing: the maintainers have published a PCIe and GPU roadmap and merged work in that direction, including PCIe bus support, hotplug discovery and VFIO wiring (Firecracker Discussion #4845). The published restrictions matter as much as the feature: VFIO requires the PCI transport, it is incompatible with virtio-mem and virtio-balloon, and the maintainers stated that snapshotting GPU devices is explicitly out of scope for the initial iteration.
Read that last clause again, because it is the crux. Firecracker’s headline advantage for agent workloads is snapshot-restore. If attaching a GPU takes snapshotting off the table, a GPU-attached Firecracker sandbox loses the very property that made you choose Firecracker.
Driver 4: Filesystem and network egress policy
Isolation of compute is the easy half. Most real agent incidents are exfiltration and lateral movement, not kernel escapes. The agent reads a credential from an environment variable it should not have had, or resolves a hostname supplied by a malicious web page and POSTs your data there.
None of the three engines solves this for you. All of them give you the primitives — a virtio network device you can attach to a tap interface you control, and a root filesystem you can mount read-only with a per-instance overlay for scratch space. What differs is convenience. Kata inherits Kubernetes NetworkPolicy and CNI, so you get a policy language for free, at the cost of trusting your CNI implementation. Firecracker gives you a raw tap device and nothing else, which is more work and more control. gVisor’s netstack terminates the network inside the sandbox, which is a genuine security benefit and occasionally a compatibility headache for exotic socket options.
Driver 5: Operational cost at scale
Hardware virtualisation needs KVM, and nested virtualisation is either unavailable or slow on many managed platforms. Firecracker and Kata therefore tend to push you toward bare-metal instances, which changes your unit economics and your capacity planning. gVisor runs on ordinary nodes, including nodes you already have, and Google offers it as a managed node-pool feature in GKE Sandbox.
Then there is the artifact problem. A microVM fleet means you now operate a kernel image, a rootfs image and — if you want the good cold starts — a snapshot store with its own versioning, garbage collection and cache-locality concerns. Snapshots are large, host-affine in practice, and go stale whenever you bump the runtime. That pipeline is a real service with a real on-call rotation, and teams routinely underestimate it.
Options Considered
Each option below is stated as the ADR expects: what it is, what it buys, what it costs, and when it is the right answer. Cold-start behaviour is the axis that separates them most sharply in agent workloads, so it is worth seeing the lifecycle end to end first.

Figure 2: Request lifecycle for a pooled, snapshot-backed sandbox.
Figure 2 shows the path that makes bursty agent traffic affordable regardless of engine. The orchestrator never boots on the request path; it claims a pre-warmed instance from a pool, and the pool refills asynchronously by restoring from a memory snapshot rather than booting. Agent code is delivered after the instance reports ready. Outbound calls traverse an egress proxy that applies an allowlist, and the instance is destroyed rather than recycled when the job exits. The engine choice determines how cheap the “fetch snapshot and restore” step is, not whether you need this shape.
Option A: Firecracker microVMs
Firecracker gives each execution its own Linux kernel on KVM, fronted by a small Rust VMM with a deliberately tiny device model — virtio-net, virtio-block, virtio-vsock, a serial console, and little else. There is no BIOS, no PCI in the classic configuration, no USB, no graphics.
What it buys: the strongest practical isolation of the three for general code execution, extremely high density, and the best cold-start story in the industry once snapshots are in play. It is also the most battle-tested at agent-relevant scale, since Lambda and several commercial sandbox providers run on it.
What it costs: you build the platform. Firecracker is a VMM, not a runtime — there is no OCI integration, no scheduler, no networking story beyond a tap device. You need something like firecracker-containerd, Cloud Hypervisor-style tooling, or your own control plane. Bare metal is effectively required. And GPU support, as covered above, is new, constrained, and currently mutually exclusive with snapshotting.
Choose it when: you are running a high-volume, CPU-only code-execution service, cold start is a product-visible metric, and you have the platform engineering capacity to own a control plane.
Option B: gVisor
gVisor intercepts the workload’s syscalls and services them in the Sentry. Modern deployments use the Systrap platform, which replaced the older ptrace platform and cut interception cost substantially.
What it buys: no KVM requirement, startup measured in tens of milliseconds, an OCI-compatible runtime (runsc) that drops into containerd, and a managed path on GKE Sandbox. It is by far the cheapest of the three to adopt if you already run Kubernetes on ordinary nodes.
What it costs: a structural performance tax and a compatibility ceiling. gVisor’s own engineering material puts single-syscall interception at roughly 800 ns against roughly 70 ns native (gVisor Systrap blog), and the commonly cited end-to-end figure is 10–30% slower than native containers on I/O- and network-heavy workloads (reported across vendor benchmarks and the gVisor performance guide; the real number is entirely workload-dependent and you should measure your own). The gVisor documentation is careful to note that raw disk I/O does not carry a significant fundamental overhead — the cost concentrates in syscall-dense and network-dense patterns, which unfortunately describes package installs and dependency resolution rather well. Not every syscall is implemented; unusual runtimes occasionally fail in ways that are hard to diagnose.
Choose it when: you want a meaningful upgrade over plain containers without building a VM platform, your workloads are compute-shaped rather than syscall-dense, and you can accept a user-space kernel rather than a hardware boundary as your trust line.
Option C: Kata Containers
Kata runs an OCI container inside a lightweight VM and presents the result to Kubernetes as a normal pod through RuntimeClass. You add runtimeClassName: kata to a pod spec and the workload lands in a VM.
What it buys: hardware isolation with none of the control-plane work. Your existing manifests, images, CNI, NetworkPolicy, service mesh, quota, admission control and observability keep working. It is the only one of the three with an officially supported, documented GPU passthrough path, and it extends naturally into confidential computing with AMD SEV-SNP or Intel TDX — a path we cover separately in our confidential containers architecture guide.
What it costs: a heavier and slower sandbox than Firecracker, a larger configuration surface (VMM choice, guest kernel, agent, rootfs, shared-filesystem mode), and the same bare-metal-or-nested-virt constraint. RuntimeClass overhead.podFixed must be set honestly or your scheduler will overcommit nodes; the values in circulation for sandboxed runtimes are on the order of a few hundred millicores and tens to low hundreds of mebibytes per pod, but you should derive yours from measurement rather than copying an example.
Choose it when: you are already a Kubernetes shop, you need GPUs in the sandbox, or you need a confidential-computing story — and a cold start in the hundreds of milliseconds is acceptable.
Option D: Plain containers (rejected)
Included because it is what most teams ship first. A container is a process with namespaces, cgroups and a seccomp profile. The workload’s syscalls hit the host kernel. It is a resource-management boundary with security side effects, not a security boundary. It is rejected here for untrusted, model-generated code, for reasons developed in the anti-patterns section below.
Decision
For a general-purpose AI agent sandbox in 2026, the defensible default is microVM-class isolation, with the specific engine chosen by your GPU requirement and your Kubernetes posture — not by benchmark numbers.

Figure 3: Selection path from workload requirements to engine.
Figure 3 encodes that as a decision path. The first branch is GPU, because it is the constraint with the fewest workarounds: if the sandbox needs a local GPU today, Kata with VFIO passthrough is the only option with a documented, vendor-supported implementation. If no GPU is needed, the next question is organisational rather than technical — an existing Kubernetes platform makes Kata’s RuntimeClass path dramatically cheaper than building a Firecracker control plane, unless your cold-start budget is tight enough that snapshot restore is mandatory. Teams that are not on Kubernetes, or that are building a dedicated execution service, land on Firecracker with a snapshot-backed warm pool. gVisor’s honest place in 2026 is as a broad default for lower-risk workloads inside a tiered scheme, with a microVM tier reserved for genuinely untrusted execution.
Three qualifications keep this decision honest. First, tiering is legitimate and common: running most workloads under gVisor and escalating suspicious or externally-triggered ones to a microVM is a reasonable cost/security trade, provided the tier assignment is not itself influenced by model output. Second, the engine is necessary but nowhere near sufficient — egress policy, credential scoping and instance lifetime do more day-to-day work than the isolation boundary. Third, this decision has a shelf life measured in months. Firecracker’s PCIe work in particular could invalidate the first branch of the tree within a release cycle.
Consequences
Adopting microVM isolation changes more than a runtime flag, and the second-order effects are where projects run aground.

Figure 4: Per-tenant control plane and blast-radius containment.
Figure 4 shows the surrounding machinery the engine does not provide. Admission and quota checks run before scheduling, so a runaway agent loop cannot exhaust the fleet. The instance mounts a read-only rootfs with a per-instance overlay for scratch, which makes tampering non-persistent by construction. Its network device is a tap that terminates at an egress proxy enforcing an allowlist with pinned DNS resolution, closing the rebinding hole that plain IP allowlists leave open. Syscall traces and audit logs stream out of the instance rather than living inside it. And the instance is destroyed at job end, never reused across tenants — because reuse is how state leaks between customers regardless of how strong your kernel boundary is.
Positive consequences. A kernel bug reachable from an ordinary syscall stops being an immediate company-wide incident. Per-tenant blast radius becomes a property you can state precisely in a security review, which matters for enterprise sales and for compliance regimes that ask what separates tenant A from tenant B. Snapshot-based pooling, once built, gives you cold starts that plain container orchestration cannot match, because you are restoring an initialised process rather than starting one.
Negative consequences. You acquire a kernel supply chain: a guest kernel to configure, build, patch and track CVEs against, separate from your host kernel. You likely acquire a bare-metal fleet, with worse elasticity and higher minimum spend than the burstable instances you were using. Debugging gets harder — kubectl exec into a microVM is not the same experience, host-level profilers do not see inside the guest, and eBPF tooling you rely on stops working across the boundary.
Observability specifically. Plan for it before you migrate, not after. The pattern that works is an in-guest agent that ships structured logs and metrics over vsock or a dedicated virtual NIC to a host-side collector, with the guest treated as untrusted throughout: the collector must not deserialise guest-supplied data into anything privileged, and per-tenant log volume must be rate-limited so a malicious workload cannot bury your pipeline.
Cost. Density is good — Firecracker’s sub-5-MiB VMM overhead means you are mostly paying for the guest kernel and the workload itself, not the virtualisation. The genuine cost is the idle warm pool. If you keep 200 pre-restored sandboxes hot to hide cold starts, you pay for 200 sandboxes whether or not anyone calls. Model that explicitly against your arrival distribution before committing to a pool size, and revisit it as traffic patterns change; the same right-sizing discipline we apply to GPU capacity in Kubernetes applies here.
Reported Figures and Their Provenance
Every number below is as reported by the cited source, not measured by us. Hardware, kernel version, workload shape and configuration all move these figures substantially, and several of them are single-author measurements rather than controlled benchmarks. Treat them as orientation, then run your own numbers on your own hardware before you commit.
| Figure | Value as reported | Source and caveat |
|---|---|---|
| Firecracker microVM boot | ~125 ms | Firecracker project material; minimal guest, no userspace init |
| Firecracker snapshot restore | low single-digit ms | Firecracker snapshot documentation; VMM-side restore, excludes orchestration |
| End-to-end snapshot-backed sandbox readiness | ~28 ms | Single practitioner write-up (dev.to, 2026); one author’s hardware |
| Firecracker per-VM memory overhead | < 5 MiB | Firecracker project material; VMM overhead only, excludes guest kernel and workload |
| Firecracker creation rate | ~150 microVMs/sec/host | Firecracker project material; host-class dependent |
| Firecracker VMM size | ~50k lines Rust vs QEMU ~1.4M lines C | Widely reported comparison; a proxy for surface, not a measurement of it |
| gVisor syscall interception | ~800 ns vs ~70 ns native | gVisor Systrap engineering blog; getpid-style microbenchmark |
| gVisor end-to-end overhead | 10–30% slower on I/O-heavy work | Reported across vendor benchmarks and gVisor’s performance guide; highly workload-dependent |
| gVisor raw disk I/O | no significant fundamental overhead | gVisor performance guide; cost concentrates at the sandbox boundary, not in block I/O |
RuntimeClass overhead.podFixed for sandboxed runtimes |
hundreds of millicores, tens–low hundreds of MiB | Configuration examples in circulation; derive yours by measurement |
The pattern worth internalising: the Firecracker numbers describe a VMM in near-ideal conditions, and the gVisor numbers describe a tax that varies by an order of magnitude with workload shape. Neither set predicts what your agent sandbox will do when it is installing a 400-package dependency tree over a cold network.
Trade-offs, Gotchas, and What Goes Wrong
Snapshot uniqueness is a real security bug, not a theoretical one. When you restore N clones from one memory snapshot, every clone resumes with identical entropy pool state, identical PRNG seeds, and potentially identical TLS session material. Academic work has documented the consequences directly (arXiv 2102.12892, “Restoring Uniqueness in MicroVM Snapshots”). If you build a snapshot-backed pool, you must reseed the guest’s randomness after restore. This is the single most commonly skipped step in homegrown microVM platforms.
Nested virtualisation quietly ruins the plan. Teams prototype Firecracker or Kata on a nested-virt-capable developer instance, measure acceptable numbers, then discover their production instance family does not expose KVM — or exposes it with performance that makes the boot-time advantage evaporate. Validate on the exact instance type you intend to buy, early.
Snapshots are host-affine and version-brittle. A snapshot taken on one CPU generation may not restore on another, because guest software captured the CPU feature set at snapshot time. You now have a snapshot-to-host-class compatibility matrix and a cache-locality problem: a restore that has to pull a multi-gigabyte snapshot across the network is not a 28 ms restore. Every runtime bump invalidates the set and forces a regeneration pass.
gVisor compatibility failures are opaque. An unimplemented syscall or ioctl surfaces as a confusing error from a library three layers up, not as “gVisor does not support this”. Budget debugging time, and keep a native-container reproduction path available so you can bisect whether the sandbox is the cause.
Time and clocks drift. Restored microVMs resume with a stale wall clock. Anything doing certificate validation, TOTP, or signed request windows will fail in ways that look like a network problem. Fix clock sync on the restore path explicitly.
The boundary does not stop the most common attack. The realistic failure mode is not a kernel escape. It is the agent being talked into using the credentials you legitimately gave it — reading an internal service, then posting the results somewhere. That is a capability and egress problem. A perfect microVM with an over-scoped token and unrestricted outbound network is less safe than a plain container with neither.
Anti-Patterns
“We use Docker, so it’s sandboxed.” A container shares the host kernel. Namespaces and cgroups were designed for resource isolation; their security properties are real but shallow, and the kernel’s syscall surface is the attack surface. For code a model wrote in response to text it found on the internet, this is not an adequate boundary. Say so plainly in your design docs rather than letting “containerised” stand in for “isolated”.
docker run --read-only as a security control. A read-only rootfs is a good hardening measure and a bad boundary. It prevents persistence, not escape, and virtually every real workload then mounts a writable /tmp and a writable working directory anyway. Combine it with --cap-drop=ALL, --security-opt=no-new-privileges, a tight seccomp profile, a user namespace and no network — and you have a hardened container, which is still a shared-kernel container.
chroot. A chroot is a filesystem view change made in the 1970s for build reproducibility. It was never a security boundary, escaping it from a process with CAP_SYS_CHROOT is a textbook exercise, and it does nothing about syscalls, networking or process visibility. It should not appear in a 2026 sandbox design.
Prompt-level guardrails as the isolation layer. Instructing the model not to exfiltrate data, or filtering its output for dangerous patterns, is defence in depth — not a boundary. A boundary is enforced by code that the adversary cannot address; a system prompt is addressable by definition, since the adversary’s input arrives in the same channel as your instructions. Injected instructions in retrieved content, tool results, or file contents will reliably defeat prompt-level controls, which is exactly the failure class analysed in our agentic AI security and prompt injection post. Read it if you want the attack detail; the point here is architectural. Guardrails reduce the rate of bad attempts. The sandbox bounds the damage when one succeeds. They are not substitutes.
Reusing sandbox instances across tenants to save cold start. A reused instance carries filesystem remnants, kernel state, cached DNS and possibly memory contents from the previous tenant. If you reuse at all, reuse only within a single tenant, and reset from snapshot rather than cleaning in place.
Giving the sandbox the orchestrator’s credentials. The sandbox should have no ambient cloud identity, no instance metadata service access, and no token it did not need for this specific job. Block the metadata endpoint at the tap device, not in the guest. This is the same principle we apply to tool-server design in our MCP server security architecture guide.
Practical Recommendations
Start by writing down your threat model in two sentences: who is the adversary, and what do they gain. If the adversary is an external user submitting prompts to a multi-tenant product, you need a hardware boundary. If it is your own engineers running semi-trusted internal scripts, gVisor is a proportionate and much cheaper answer.
Then pick the engine from the constraint that binds hardest — GPU first, Kubernetes posture second, cold-start budget third. Resist choosing on benchmark numbers you did not produce. Build the surrounding control plane before you optimise the engine: egress allowlisting, credential scoping, instance destruction and per-tenant quotas deliver more risk reduction per engineering week than shaving 40 ms off a restore.
A concrete sequence that works:
- [ ] Write the threat model and the blast-radius statement first; both should fit on one page.
- [ ] Validate KVM availability and measure boot or restore on your exact production instance type before committing.
- [ ] Ship a single-engine v1 with no snapshots, no pooling and a generous timeout; get the lifecycle correct before optimising.
- [ ] Put every sandbox behind an egress proxy with a default-deny allowlist and pinned DNS from day one.
- [ ] Mount rootfs read-only with a per-instance overlay; destroy on exit; never reuse across tenants.
- [ ] Block the cloud metadata endpoint at the network device, not inside the guest.
- [ ] Add snapshot-backed pooling only once cold start is a measured product problem — and reseed guest entropy on every restore.
- [ ] Set RuntimeClass
overhead.podFixedfrom measurement if you are on Kata, or your scheduler will overcommit. - [ ] Instrument per-tenant sandbox count, lifetime and egress-denial rate; the denial rate is your best early signal of a compromised agent.
- [ ] Re-run the engine decision every two quarters — GPU support in particular is moving fast.
Frequently Asked Questions
Is Firecracker more secure than gVisor for running LLM-generated code?
For untrusted multi-tenant code, most practitioners treat Firecracker as the stronger boundary, because escaping it requires breaking a small Rust VMM or KVM rather than a user-space kernel implementation. gVisor is a large improvement over plain containers and its Sentry blocks most syscall-reachable kernel bugs, but its boundary is software in the same privilege domain rather than hardware virtualisation. The honest answer is that both are defensible and the deciding factors are usually operational: GPU needs, Kubernetes fit, and how quickly you can patch.
Can Firecracker do GPU passthrough in 2026?
Not in the settled, production-documented way Kata can. Firecracker historically shipped with no PCI bus and no VFIO by design, and PCIe plus device-passthrough support is work in progress with a published roadmap. Two published restrictions matter most: VFIO passthrough requires the PCI transport and is incompatible with virtio-mem and virtio-balloon, and snapshotting GPU-attached microVMs was explicitly out of scope for the initial iteration. Since snapshots are Firecracker’s main cold-start advantage, a GPU-attached Firecracker sandbox forfeits much of the reason to choose it.
How much slower is gVisor than a normal container?
The commonly cited range is 10–30% on I/O- and network-heavy workloads, and that figure appears across vendor benchmarks and gVisor’s own performance documentation. The structural source is syscall interception: gVisor’s engineering blog puts a single intercepted syscall around 800 ns against roughly 70 ns native. Compute-bound work sees far less impact, and gVisor’s documentation notes that raw disk I/O carries no significant fundamental overhead. Syscall-dense workloads like package installs and dependency resolution sit at the painful end of the range.
Does Kata Containers work as a drop-in Kubernetes runtime?
Largely yes, which is its main selling point. You install the Kata runtime on nodes, define a RuntimeClass, and add runtimeClassName to the pod spec; existing manifests, images, CNI and NetworkPolicy continue to work. The caveats are real though: you need KVM, so bare metal or nested-virt-capable nodes; you must set overhead.podFixed accurately or the scheduler will overcommit; and host-path mounts, privileged pods and some device plugins behave differently across the VM boundary.
Why is a read-only Docker container not enough for an AI agent sandbox?
Because read-only mode addresses persistence, not escape. The workload still shares the host kernel and can still reach the full syscall surface, so a kernel privilege-escalation bug remains exploitable. Almost every real workload then mounts writable scratch directories, weakening even the persistence property. Hardened containers with dropped capabilities, seccomp, user namespaces and no network are a meaningful improvement and still not a hardware boundary — they should be your floor, not your ceiling.
Do prompt guardrails reduce the need for a sandbox?
No. Guardrails operate in the same channel the adversary writes to, so injected instructions in retrieved documents, tool outputs or file contents can override them. They lower the rate of bad attempts, which is worthwhile, but they cannot bound the consequences of the attempts that succeed. Treat guardrails as a filter and the sandbox plus egress policy as the enforcement layer; if you can only build one, build the enforcement layer.
Further Reading
- Agentic AI security and prompt injection defences for 2026 — the attack side of the same problem, and why guardrails are not a boundary.
- Confidential containers on Kubernetes: architecture guide — where Kata plus SEV-SNP or TDX takes the isolation story next.
- MCP server security architecture — scoping the credentials and tools an agent gets in the first place.
- LangGraph vs CrewAI vs Agents SDK vs Pydantic AI — the orchestration layer that calls into the sandbox.
- gVisor performance guide — the primary source for gVisor’s overhead characteristics.
- Firecracker snapshot support documentation — restore semantics, limitations and the uniqueness warning.
- AI Code Sandboxes: A Comparative Security Study, Part 1 — engine-level attack surface, CVE history and patch cadence.
By Riju — about
