For an AI agent that runs code nobody has reviewed, a microVM gives the strongest boundary, gVisor gives a middle ground that keeps container tooling, and a plain container is the floor rather than the ceiling. Which one to pick depends on how much the agent’s code could do if it got out, how much overhead you can afford per task, and whether the task needs a device such as a GPU.
This post walks through the three isolation models, what each boundary is made of, what it costs, and how to decide. It is part of the guide to sandboxing AI agents on Linux, which also covers why identical images don’t give identical runs and a practical starting setup with Incus. Everything here is general, public practice.
Why does the isolation choice matter more for agents?
An ordinary workload runs code that someone wrote, reviewed and deployed. An agent runs code it wrote a moment ago, in response to a task description and whatever it read along the way. You cannot review that code before it runs, because it does not exist until the agent produces it. The sandbox is the review.
That is why sandbox escape is now measured as a capability of the model itself, not only as a property of the container. SandboxEscapeBench, published in March 2026, is an open benchmark that measures how far a language model can break out of the Docker and OCI container sandboxes in which agents are commonly deployed and evaluated. Its threat model is a motivated adversarial agent with shell access inside a container, and it covers escape mechanisms across four categories: misconfiguration, privilege allocation mistakes, kernel flaws, and weaknesses in the runtime or orchestration layer. The benchmark runs as an Inspect AI capture-the-flag evaluation inside a nested sandbox, where the outer layer holds the flag and has no known vulnerabilities. The authors report that when vulnerabilities are added, models are able to identify and exploit them, and they argue that this kind of evaluation is needed if sandboxing is to keep providing the encapsulation that highly capable models require (SandboxEscapeBench, 2026).
Two things follow. First, the four categories in that benchmark map directly onto what each isolation model does or does not defend. A kernel flaw is fatal to a container that shares the host kernel, less so to a workload behind a separate guest kernel. A misconfiguration, such as a mounted host socket, defeats any of the three. Second, the finding that models exploit vulnerabilities that are present means the question is not whether an agent would try, but what it would find.
How do containers isolate a process?
A Linux container is a set of kernel features applied to an ordinary process tree. Namespaces give the processes their own view of process IDs, mounts, network interfaces, hostnames and users. Control groups cap what they can consume. Capabilities restrict which privileged operations root inside the container may perform. A seccomp filter restricts which system calls the kernel will accept from them. Put together, the processes see a private machine, but every system call they make is handled by the same kernel that runs the host.
That shared kernel is the point and the problem. It is why containers start in milliseconds and cost almost nothing beyond the processes themselves. It is also why every kernel vulnerability reachable through an allowed system call is a potential escape. The kernel’s system call interface is large, and a container exposes most of it.
The Incus documentation describes system containers as full Linux distributions running on a shared kernel, implemented with LXC, and lists low overhead and dense packing as their main advantage (Incus documentation, 2026). On the security side it says containers are unprivileged by default, with the container’s root confined to the permissions of an ordinary host user through a user namespace, and it warns that privileged containers “aren’t root safe”: a root user inside one could disrupt the host and find ways to escape confinement. It recommends not using privileged containers unless required (Incus security documentation, 2026).
For an agent, the practical rule is that a container is acceptable when the worst thing the agent could reach by escaping is something you already consider the agent’s to reach. A throwaway build box with no credentials, on a host that holds nothing else, is a reasonable container workload. A host that also runs your production database is not.
How does gVisor change the picture?
gVisor keeps the container model but inserts a kernel between the workload and the host. Its documentation describes the Sentry, the largest component, as an application kernel written in Go that implements system calls, signal delivery, memory management and threading itself. It intercepts the application’s system calls and acts as the guest kernel; it does not pass system calls through to the host. File access that has to leave the sandbox goes through a separate host process, the Gofer, which mediates that access. The whole thing ships as an OCI runtime called runsc, which plugs into Docker and Kubernetes (gVisor documentation, 2026).
How the interception works depends on the platform. The platform guide lists three. The default since mid-2023 is systrap, which uses seccomp’s trap facility to catch system calls and is the better choice inside a virtual machine or on a machine without hardware virtualisation. The KVM platform lets the Sentry act as both guest operating system and virtual machine monitor and gives the best performance on bare metal. The older ptrace platform runs anywhere ptrace works but carries high context-switch overhead, is no longer supported, and is expected to be removed (gVisor Platform Guide, 2026).
The security model is explicit about what this buys. No system call is passed directly to the host; each supported call has its own implementation in the Sentry. The Sentry’s own access to the host is limited to its Gofer connection, a small set of host calls, and a virtual network device, and the sandbox operates in an empty mount namespace. The stated design goal is to minimise the system-call attack surface through multiple layers of defence (gVisor Security Model, 2026).
The cost is also explicit. The architecture comes “at the price of reduced application compatibility and higher per-system call overhead”, gVisor does not implement every system call or every file under /proc and /sys, and it may perform poorly for system-call-heavy workloads (gVisor documentation, 2026). The performance guide separates structural costs, which come from the design and fall on system calls, from implementation costs in subsystems such as the network stack and the file system layer that are less optimised than mature equivalents. Raw memory access carries no extra cost, CPU-bound work such as data processing runs natively, and the workloads that suffer most are those that do little work per system call: small-request web services, static file serving and in-memory data stores (gVisor Performance Guide, 2026).
Agent workloads sit awkwardly on that spectrum. Compiling, running a test suite and installing packages are all heavy on file and process system calls. Expect them to be noticeably slower under gVisor than in a plain container, and test the specific toolchain before committing, because a missing system call or /proc entry shows up as a confusing build failure rather than a security event.
What does a microVM such as Firecracker give you?
A microVM gives the workload its own kernel on a small, hardware-virtualised machine. The boundary is the hypervisor and the processor’s virtualisation extensions rather than the host kernel’s system call interface.
Firecracker is the reference example. Its project page describes it as open-source virtualisation technology for secure, multi-tenant container and function-based services, running in user space on Linux KVM. It emulates only five devices: a virtio network device, a virtio block device, a virtio socket, a serial console and a minimal keyboard controller used only to stop the machine. The page claims less than 5 MiB of memory overhead per microVM, boot in under 125 ms, and the ability to create up to 150 microVMs per second per host. It was built at Amazon Web Services for AWS Lambda, started from the crosvm virtual machine monitor, is written in Rust, and runs on 64-bit Intel, AMD and Arm processors with hardware virtualisation support (Firecracker project, 2026).
The design document is clearer than most about the threat model. It treats all guest vCPU threads as running malicious code from the moment they start. The first layer of isolation is the KVM and Firecracker virtualisation boundary. For defence in depth, Firecracker should also run constrained at the process level: seccomp filters applied to each thread before any guest code runs, a companion program called the jailer that sets up a chroot and resource controls with elevated privileges and then drops them before executing the unprivileged Firecracker binary, and namespaces around the result. The document also states plainly that Firecracker does no network traffic filtering; guest egress has to be filtered on the host (Firecracker Design, 2026).
The Incus documentation describes the same category from a different angle. Its virtual machines are full virtualised systems with their own kernel, implemented with QEMU; anything that needs a different kernel or its own kernel modules belongs in a VM, and full PCI device pass-through works properly only with VMs (Incus documentation, 2026). That last point matters for agents that need a GPU.
The price of a microVM is memory per instance for the guest kernel and whatever the guest runs, slower starts than a container (though still well under a second for a Firecracker-style machine), and more work to get files and results in and out, because there is no shared filesystem unless you add one.
How do the three compare?
| Containers | gVisor | MicroVMs | |
|---|---|---|---|
| What enforces the boundary | Host kernel: namespaces, control groups, capabilities, seccomp | A user-space kernel (the Sentry) that serves the workload’s system calls | A hypervisor and a separate guest kernel |
| What the host kernel sees | Every system call the workload makes | A small set of host calls from the Sentry | Virtual device I/O through the hypervisor |
| Kernel flaw in the host | Direct escape risk | Reachable only through the Sentry’s reduced surface | Reachable only through the hypervisor and device emulation |
| Startup | Milliseconds | Milliseconds | Hundreds of milliseconds to seconds |
| Memory per instance | The processes only | Processes plus the Sentry | Processes plus a guest kernel |
| CPU-bound work | Native | Native | Native, with virtualisation overhead on some operations |
| System-call-heavy work | Native | Slower; depends on platform and subsystem | Native inside the guest; I/O crosses the device boundary |
| Compatibility | Full | Partial; not every system call or /proc entry | Full, with a guest kernel you control |
| GPU and device access | Shared with the host | Limited | Pass-through, which Incus documents as VM-only |
| Examples | Docker, Podman, LXC, Incus system containers | runsc | Firecracker, Cloud Hypervisor, Incus and QEMU virtual machines |
The rows on startup, memory and compatibility are general properties of the three designs. The specific claims about gVisor’s costs and Firecracker’s overheads come from their own documentation, cited above, and are worth checking against your hardware rather than taken as fixed.
Which should you choose for an agent?
The decision turns on three questions.
What can the agent reach if it escapes? If the answer is “nothing it couldn’t reach anyway”, a container is fine. If the host holds credentials, other tenants’ data or a route into your network, you want the hypervisor boundary. SandboxEscapeBench’s categories are a useful checklist here: ask which of misconfiguration, privilege mistakes, kernel flaws and orchestration weaknesses each option leaves open on your particular host (SandboxEscapeBench, 2026).
How many runs, and how long is each? A lab that runs thousands of short tasks a day cares about startup and density, which favours containers and gVisor. A lab that runs a few long agent sessions cares little about a one-second start, which makes the microVM’s cost negligible.
Does the task need a device? GPU training or inference inside the sandbox pushes you towards a VM with pass-through. Pure CPU work, which covers most coding and operations tasks, has no such constraint.
A reasonable default: a fresh microVM or an unprivileged system container per task, created from a prepared base, destroyed afterwards. Use gVisor where you must keep container tooling (Kubernetes, existing OCI images) but cannot accept a shared kernel. Use a plain container only where the host is itself disposable.
| Situation | Reasonable choice |
|---|---|
| Public code, disposable host, no credentials | Unprivileged container |
| Existing Kubernetes or Docker workflow, untrusted code | gVisor (runsc) |
| Untrusted code, shared host, credentials nearby | MicroVM |
| Agent needs a GPU or kernel modules | VM with device pass-through |
| Short tasks at very high volume | gVisor or container, with the host treated as disposable |
What does none of them protect against?
Every isolation layer has a floor beneath it, and the documentation of each project says so.
gVisor’s security model states that it relies on the host operating system and the platform for defence against hardware-based attacks, that it does not in general protect against hardware side channels, and that resource exhaustion is handled by host control groups rather than by the sandbox. It closes with the line that a sandbox is not a substitute for a secure architecture (gVisor Security Model, 2026). Firecracker’s design document says the project does no network filtering, so an agent in a microVM with an unfiltered network interface is not contained in any meaningful sense (Firecracker Design, 2026). Incus warns that local access to its daemon socket always grants full control of Incus (Incus security documentation, 2026), so an agent that can reach that socket owns every instance on the host.
Three failure modes cut across all three models.
Misconfiguration. A mounted host directory, a passed-through socket, a container run as privileged, an over-broad capability set. This is the first category in SandboxEscapeBench for a reason: it needs no exploit, only an agent that notices.
Credentials. Agents are usually given tokens for the tools they use. An escape, or simply a network connection, carries those tokens with it. Isolation of the process does nothing for a secret that the process legitimately holds.
Network. None of the three isolates the network by default. The container or VM gets an interface, and whether that interface can reach the internet is decided by host configuration. For an agent, “no route out unless a task explicitly needs one” is the setting to start from.
Can you combine them?
Yes, and the combinations are better than any single layer.
Containers inside a microVM is the common pattern: the VM is the security boundary, and the container inside it provides the familiar image format, filesystem layout and tooling. The agent’s processes see a container; the host sees a VM. The gVisor platform guide notes that systrap is the better choice inside a VM, so gVisor-in-a-VM is also practical where you want both the reduced system-call surface and a hypervisor behind it (gVisor Platform Guide, 2026).
Firecracker’s own design is layered in this way: the virtualisation boundary first, then seccomp, control groups, namespaces and privilege dropping around the Firecracker process itself (Firecracker Design, 2026). The principle generalises. Whatever runs the sandbox should itself be sandboxed, because the component that manages isolation is the component an escape would target next.
Incus is a convenient way to get both options under one tool, since it manages system containers and virtual machines with the same images, profiles and commands (Incus documentation, 2026). A lab can start every task in a container and move specific task families to VMs as their risk becomes clear, without changing the workflow around them.
What this means for teams running agents
Treat the isolation model as a decision you make per task family, not a default inherited from whatever tooling was already installed. For most agent work, a fresh unprivileged system container or a microVM per run, with no outside network and no credentials beyond what the task needs, covers the realistic threats. gVisor is the right answer when you are tied to container orchestration and cannot accept a shared kernel. In every case, the documentation of the project you choose is explicit about what it does not cover, and those gaps, hardware side channels, network egress, misconfiguration and the management socket, are where an agent that wants out will look.
The broader guide to sandboxing AI agents on Linux sets this choice alongside the other two problems a lab has to solve: why two runs of the same image diverge, and how to arrange instances, snapshots, networks and limits so each run starts clean and ends bounded. Evidence Every factual claim on this page links to one of these. Numbers in brackets refer to this list. Why two runs of an AI agent on one container image diverge: inference is nondeterministic even at temperature 0, precision matters and timing differs. A starting Incus setup for AI agents: unprivileged containers, a profile with resource limits, a bridge with no route out and a snapshot before each run.Sources
More on this topic
Same image, same behaviour? Not for agent runs
Incus for an agent lab: a practical starting setup