All posts
6 min read

Firecracker vs gVisor vs Containers: Choosing Isolation for Untrusted Code

Containers share a kernel; gVisor intercepts it; Firecracker gives each workload its own. How the three isolation models actually work, what each costs in performance and compatibility, and how to match the boundary to the threat — for AI agents, multi-tenant platforms, and code execution.

infrastructuresecurityisolationarchitecture

Every platform that runs other people's code — or code an AI wrote a minute ago — has to answer one question: what stops that code from reaching things it shouldn't?

The three common answers are standard containers, gVisor, and Firecracker microVMs. They're often presented as a ladder, from "fast but weak" to "slow but strong." That's roughly right and misses the interesting part: they draw the boundary in different places, and the right choice depends on what you're defending against.

The thing all three are protecting: the kernel

When a program on Linux opens a file, allocates memory, or makes a network connection, it asks the kernel through a system call. The kernel is enormous, complex, and runs with full privileges. A bug in it is a way out of whatever box the program is in.

So every isolation model is really an answer to: how much of the host kernel can the untrusted code talk to?

Standard containers: share the kernel, restrict the view

A container is an ordinary process on the host, with Linux features limiting what it can see and use:

  • Namespaces give it its own view of processes, filesystems, network interfaces, and users.
  • cgroups cap its CPU, memory, and process count.
  • seccomp filters which system calls it's allowed to make.
  • Capabilities strip away root-level powers, even for a root user inside.

Crucially, every container on a host talks to the same kernel. The kernel enforces the walls — and is also the shared attack surface. A kernel vulnerability reachable through an allowed system call can become a container escape.

Strengths: near-zero overhead, instant startup, full compatibility, enormous tooling ecosystem.

Weakness: the boundary is only as strong as the shared kernel and the configuration. A privileged container, a mounted Docker socket, or a host path mount undoes most of it.

Good for: workloads you broadly trust — your own services, or tenants you have a contractual relationship with — especially when hardened: non-root user, no extra capabilities, a strict seccomp profile, resource limits, no host mounts. (We go into how containers compare with plain Linux users in Docker vs Linux Users for Multi-Tenant Isolation.)

gVisor: a kernel in between

gVisor, from Google, puts a user-space kernel (called the Sentry) between the application and the host. The application's system calls are handled by gVisor, which implements Linux behaviour itself and makes only a small, restricted set of calls to the real host kernel.

The untrusted code never talks to the host kernel directly. To escape, it has to break gVisor and then break through the much narrower interface gVisor uses.

Strengths: a much smaller host attack surface, while still feeling like a container — it plugs into container tooling as an OCI runtime (runsc), starts quickly, and doesn't need hardware virtualisation.

Weaknesses: system-call-heavy workloads pay a performance cost, since every call goes through gVisor's implementation. And because gVisor reimplements Linux, not every feature or edge case behaves identically; some software needs adjustment.

Good for: running untrusted code at scale on infrastructure where you can't, or don't want to, use virtual machines — and where the workload is compute-heavy rather than I/O-heavy.

Firecracker: a tiny virtual machine per workload

Firecracker, from AWS, is a minimal virtual machine monitor built on Linux KVM. Each workload gets its own kernel inside a lightweight VM, with only a handful of emulated devices. It was built to run AWS Lambda and Fargate.

The untrusted code can attack its own guest kernel all it likes; that kernel belongs to it alone. To reach the host, it has to escape the virtual machine — through hardware virtualisation and Firecracker's deliberately tiny device model.

Strengths: VM-grade isolation — the strongest boundary of the three — with startup measured in fractions of a second and small memory overhead, which made per-workload VMs practical.

Weaknesses: needs hardware virtualisation (bare metal or nested virtualisation), more operational complexity than containers, a minimal device model (no GPU passthrough, for example), and fewer off-the-shelf tools.

Good for: hostile multi-tenancy — running arbitrary code from strangers — where the cost of an escape is catastrophic.

Side by side

Containers gVisor Firecracker
Kernel Shared host kernel User-space kernel in front of host Own guest kernel per workload
Host attack surface Large (filtered by seccomp) Small Very small (hypervisor + minimal devices)
Startup Milliseconds Fast Fraction of a second
Overhead Minimal Higher on syscall-heavy work Small memory and boot cost
Compatibility Full Most software; some gaps Full Linux inside the VM
Needs virtualisation hardware No No Yes
Typical use Trusted services, hardened tenants Untrusted code on shared hosts Hostile multi-tenancy, serverless

(Kata Containers is another option worth knowing: it runs containers inside lightweight VMs, using hypervisors including Firecracker, to combine container tooling with VM isolation.)

Matching the boundary to the threat

The useful question isn't "which is most secure?" It's "who is on the other side of the boundary?"

  • Arbitrary code from anonymous users — a public code runner, a playground, a per-request AI code tool: assume hostile. Use microVMs or gVisor.
  • AI agents writing and running code in your project: the agent isn't malicious, but it makes mistakes and can be manipulated by content it reads (prompt injection). The main risks are damage to the project and access to credentials, not kernel exploits. A hardened container with strict limits, no host access, and minimal credentials handles most of it; stronger runtimes add depth.
  • Known customers on a multi-tenant platform: hardened containers are common and reasonable, with gVisor or microVMs where tenants run arbitrary code or data is especially sensitive.
  • Your own internal services: standard containers, hardened sensibly.

Isolation is more than the runtime

Whichever runtime you pick, most real-world escapes and incidents come from configuration rather than kernel bugs:

  • A mounted Docker socket is root on the host, in any runtime.
  • Privileged mode or extra capabilities remove most container protections.
  • Host path mounts give direct access to the host filesystem.
  • Credentials in the environment reach whatever they reach, however strong the sandbox.
  • Missing resource limits let one tenant starve the others.
  • Unrestricted network egress lets code exfiltrate data or attack internal services.

A well-configured container beats a badly configured microVM. Get these right first; then choose the runtime for the threat you actually face.

The summary

Containers restrict what a process can see while sharing the host kernel. gVisor puts its own kernel in the way. Firecracker gives each workload a kernel of its own. Each step buys a smaller attack surface for some cost in performance, compatibility, or operational effort.

For hostile code, pay for the strong boundary. For code you broadly trust — including an AI agent working on your own project — hardened containers with strict limits and minimal credentials are usually the right trade, and the remaining risk lives in configuration and credentials, not the runtime. That's also why we argue, in The Sandbox Is the Wrong Abstraction for AI Coding Agents, that isolation strength and environment lifetime are separate decisions.


EasySpawn runs each workspace in its own container as a non-root user, with enforced CPU, memory, and process limits, and no access to the host filesystem or Docker socket. See how it works or join the waitlist.

Related: Docker vs Linux Users for Multi-Tenant Workspace Isolation · Running Claude Code Unattended

Keep reading