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.
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
Docker vs Linux Users for Multi-Tenant Workspace Isolation
Separate Linux users look like a cheap way to isolate tenants until you try to enforce a CPU limit. A walkthrough of why containers win for multi-tenant development workspaces — and how to verify the limits are real.
Claude Code --dangerously-skip-permissions: When It's Safe, and What to Use Instead
An agent that stops to ask permission can't work while you're away. An agent that never asks can delete things. How Claude Code's permission modes and allow/deny rules work, when skipping prompts is reasonable, and what has to be true of the environment first.