Why Coding Agents Need Isolated VMs Instead of Shared Containers
Agents running untrusted code need stronger isolation than containers can provide.

Coding agents install packages, spawn subprocesses, write and delete files, make network requests, and build and run their own containers while a human is doing something else. That is the subject of this piece: what agents do when nobody is watching each step, and why that behavior demands a different kind of isolation than the one most teams already have in place.
Traditional tool calls, searching an index, querying a database, sending an email, belong to a closed set, but code execution is a general-purpose programming environment, so the space of things an agent can do once it starts writing and running its own code is unbounded.
Consider a task as plain as "analyze this CSV." It's a structural consequence of giving a system the ability to write and execute arbitrary code in service of a goal stated in natural language.
Three threat categories follow directly from that arbitrary execution, and all three appear in ordinary agent work, not just adversarial testing. Prompt injection is the first: malicious content sitting in a README, in a dependency's metadata, or in any file the agent happens to read can hijack what the agent does next, because the agent cannot reliably tell instructions from data. Resource abuse is the second: an infinite loop, a fork bomb, or a script that fills a disk can take down an entire host, and an agent chasing a goal has no built-in sense of when to stop. Lateral movement is the third: reading environment variables, reaching secrets it was never meant to touch, or brushing up against data belonging to another tenant on the same machine.
None of this is unusual or rare in agent workflows. That habit is harmless on its own. It becomes a serious problem the moment it runs inside an isolation model that was never designed for code nobody reviewed first.
How containers achieve isolation
Containers rest on four Linux kernel mechanisms: namespaces, cgroups, capabilities, and seccomp. Every container, no matter how it's configured, still executes against one shared kernel.
Namespaces virtualize process IDs, network stacks, and filesystem mount points, so a process inside a container sees its own tiny world. Cgroups cap how much CPU, memory, and I/O a container can consume, which keeps one noisy workload from starving its neighbors. Each of these is a real and useful control, and together they make containers the right tool for code whose behavior is already known: an internally built service, a reviewed application, anything that passed through a team's own CI pipeline before it ran.
The filter, the cap, the virtualized view, all of it is still enforced by one kernel that the host and every container on that host share. Namespaces change what a process can see, but when a kernel-level boundary fails, the blast radius is the whole machine.
Why the shared kernel is exploitable by agent behavior
The qualities that make containers adequate for a known, reviewed workload are precisely the qualities that agent behavior sets aside. A container boundary assumes the code running inside it has already been through some kind of scrutiny, by a developer, a linter, a review process, before it ever executes. Agents generate code at runtime, from natural-language instructions, and run it immediately. There is no step in between where a human looks at what's about to happen.
Treating that runtime-generated code as though it carries the same trust level as reviewed software is the first way agent behavior makes the shared kernel exploitable. A hardened container profile does real work against malware someone wrote in advance and tried to sneak past a scanner. It does less for code an agent wrote thirty seconds ago to solve a problem the agent hasn't seen before, because nobody, including the agent, can fully predict what that code will attempt.
The second amplifier is nesting. It's a routine part of how agents actually work day to day.
Agents executing arbitrary, untrusted code are the most likely path to actually triggering such a bug, simply because they generate so much code nobody has seen before, at a volume and speed no human review process can keep up with.
CVE-2026-25592 makes the structural argument concrete. Disclosed by Microsoft, with an accompanying research post, it showed how prompt injection inside an agent framework escalated all the way to host-level remote code execution. None of this required a sophisticated zero-day hunt to begin with, either. Real incidents in production commonly start with an exposed port, an access control left too permissive, or a debug setting nobody turned off, and an agent operating on its own is more likely than a human developer to stumble into one of those states simply by doing its job.
What hardware-enforced isolation changes
MicroVMs draw their first security boundary in hardware rather than in software, and that is a categorically different trust model from anything namespaces and cgroups can offer. Instead of one shared kernel enforcing separation between many workloads, each workload gets its own guest kernel, running behind hardware virtualization, that absorbs whatever the workload does before the host kernel is ever in reach.
A kernel exploit triggered inside one VM stays inside that VM. It cannot reach the host, and it cannot reach any other VM running alongside it. The Firecracker design paper describes this as moving the security-critical interface onto a boundary backed by hardware and comparatively simpler software, while keeping the virtual machine monitor itself inside the trust boundary. Firecracker also keeps its virtual device model narrow by design, so the surface it exposes to the host is smaller than what a general-purpose virtual machine stack would expose, cutting down the attack surface even further.
That change answers each of the three amplifiers described above, directly. Runtime-generated code, however hostile, runs inside its own kernel and cannot reach a neighbor even if it finds a bug in that kernel. An agent building a container inside its VM is still boxed in by a kernel boundary it has no way to cross. A kernel CVE discovered inside one VM stays scoped to that VM; it does not automatically become a multi-tenant incident the way the same bug would on a shared container host. None of this makes microVMs invulnerable. It changes the size of the failure from "the whole host and every tenant on it" to "one isolated workload," which is the difference that actually matters for a platform running code nobody reviewed in advance.
The isolation spectrum: where gVisor, Kata Containers, and Firecracker each sit
Hardware-backed isolation isn't one single product teams either adopt or skip. gVisor, Kata Containers, and Firecracker each draw the boundary in a different place, and the right choice depends on how untrusted the code an agent produces actually is.
gVisor runs as a user-space kernel. Google uses it for Agent Sandbox on GKE, and Modal uses it to run large numbers of concurrent sessions. The tradeoff is syscall compatibility: not every piece of Linux software runs correctly against an emulated kernel, so some workloads need testing before they can be trusted to gVisor.
Kata Containers sit further along the spectrum, offering hardware-enforced isolation with a dedicated kernel per workload, built on KVM-based hypervisors such as QEMU, Cloud Hypervisor, Firecracker, or Dragonball. Kata is the recommended fit for regulated industries and for zero-trust multi-tenant Kubernetes environments, where the cost of a shared-kernel failure outweighs the cost of a slower start.
The decisions hyperscalers have already made are instructive. None of the three reached for ordinary containers when it came time to isolate AI workloads specifically.
A simple heuristic follows from all of this. Arbitrary binaries, or package installs an agent chose on its own without anyone vetting them first, call for hardware virtualization through microVMs as the floor, not the ceiling.
What snapshot restore and per-agent VMs change about performance
The usual objection to microVMs is startup time, and it mostly rests on comparing a warm container to a VM booting cold. That comparison isn't the one that matters for agent workloads, and once it's replaced with a fairer one, the objection mostly falls apart.
The fair comparison measures a warm container against a restored microVM, from the moment of request to the moment the environment is actually runnable. Snapshot restore resumes a pre-initialized memory image instead of booting a kernel from zero, and the latency gap between a warm container and a restored microVM shrinks a great deal once that technique is in play.
There's a real cost that doesn't disappear: a microVM adds a VMM process and a configured block of guest RAM for every instance it runs, so density arithmetic has to account for that overhead. The number worth comparing, though, is how many isolated executions a given host can sustain at the trust boundary a team actually needs, not how many bare processes that host could otherwise hold. Whether that added cost is worth paying comes down to whether the security model it buys matches the scale and risk profile of the team running it.
The ZeroBoot project put a number on how far this gap has already closed, demonstrating a pre-snapshotted Linux microVM forked in under 1 millisecond using copy-on-write KVM fork. The engineering distance between container startup and VM startup keeps narrowing, which shortens the cold-start penalty precisely for the short-lived, repeated-invocation pattern agent workloads follow. Cloud agent tasks are background work: they return a pull request rather than completing an interactive line of code while a developer waits. Startup latency matters far less to a background task than to a synchronous copilot suggestion. The performance objection is weakest in exactly the use case agents actually represent.
What a properly configured agent VM environment requires
Isolation is necessary, but it does not by itself make an environment one where an agent can do real engineering work. The VM also has to be configured so the agent can actually act inside it.
Four structural requirements define a production-grade agent sandbox. Compute isolation means a separate kernel for every agent run, never shared across tenants or concurrent tasks. Filesystem boundaries mean the agent can read and write only the paths it's been given, and system directories are out of scope. Network controls mean outbound traffic is filtered against an allowlist rather than left open, so an agent can make the HTTP calls its task requires without being able to reach anywhere else on the internet. Per-agent storage quotas prevent a single runaway process, a recursive write loop or a dependency tree that explodes in size, from consuming disk space that belongs to other workloads.
Containment alone isn't enough to make an agent useful. An agent that has to reinstall its entire toolchain from scratch every time it runs is slower and less reliable than one that resumes from a configured, ready state. Inside that sandbox, agents need the ability to install packages, run services, drive a browser, and check their own work, because a stripped-down execution runtime that can only run a script is not equivalent to a developer's actual machine, and agent work increasingly looks like the kind of work a developer's machine is built for.
Every action inside the VM should be logged and auditable: which agent ran, which model it used, which harness drove it, how long the run took, and what it produced. That record is what turns agent work into something a team can measure, rather than a black box that happens to return pull requests.
Why the environment matters as much as the model at team scale
Once a team moves past one engineer trying an agent on one task and starts running agents across many engineers, many repositories, and many concurrent background jobs, the environment becomes the variable that decides whether any of that output can be trusted. The model matters. The environment running it matters just as much, because the environment is where every security property and every performance assumption discussed above actually gets enforced or quietly abandoned.
An agent that mutates a shared environment leaves something behind for the next run to inherit: a dependency conflict, leftover filesystem state, a cached credential nobody meant to persist. That residue makes results hard to reproduce and makes a reviewer's job harder, because the reviewer can no longer be sure what conditions actually produced the change under review. Isolated VMs, one per run, remove that uncertainty: a pull request comes back from a known, bounded environment whose starting state the team controls completely, not from a shared context some earlier agent run may have altered.
Isolation is also what makes it safe to use more than one agent harness on the same codebase. In a shared environment, switching from one harness to another risks carrying over state the previous tool left behind. With a fresh, isolated VM for every run, a team can run Claude Code, Codex, or Opencode against the same repository without one contaminating the next. The attribution record described earlier, which agent, which model, which credential, how long it ran, only means what it claims to mean when each run is actually isolated. In a shared environment, attribution is a guess dressed up as a log.
Isolated VMs are baseline infrastructure that turns cloud coding agents into something an engineering team can actually delegate real work to, instead of a tool kept at arm's length and double-checked line by line.

