# gVisor vs Kata Containers vs Firecracker for Untrusted Code

> Pick by boundary and by what the code needs. gVisor puts a user-space kernel in front of the host, needs no KVM, but implements only part of Linux. Kata Containers runs each Kubernetes pod in its own VM. Firecracker is a minimal microVM monitor you build your own platform around.

Source: https://sandboxer.com/guides/gvisor-vs-kata-vs-firecracker-for-untrusted-code. Updated 2026-10-31.

**Short answer:** Pick by boundary and by what the code needs. gVisor puts a user-space kernel in front of the host, needs no KVM, but implements only part of Linux. Kata Containers runs each Kubernetes pod in its own VM. Firecracker is a minimal microVM monitor you build your own platform around.

*Sources: each project's own docs and repository, linked inline and read on the date above. Sandboxer runs none of the three: each Sandboxer sandbox is a Cloud Hypervisor microVM. The probes further down ran in one of those.*

If you're still choosing between a plain container and a VM, start with [Container vs microVM](/guides/container-vs-microvm-for-untrusted-code). This page compares three open-source projects used for the next step.

## They aren't the same kind of thing

- **gVisor** is an OCI runtime (`runsc`) with an "application kernel" written in Go that "runs in userspace". It intercepts the application's system calls and "acts as the guest kernel, without the need for translation through virtualized hardware". Its docs say it is "not a VM in the everyday sense of the term". ([What is gVisor?](https://gvisor.dev/docs/))
- **Kata Containers** is an OCI runtime that starts a lightweight VM with its own guest kernel and runs your container inside it. "Kata Containers represents a Kubelet pod as a VM." It drives a hypervisor you choose: QEMU, Cloud Hypervisor, Firecracker, Dragonball or StratoVirt. ([Kubernetes support](https://github.com/kata-containers/kata-containers/blob/main/docs/design/architecture/kubernetes.md), [hypervisors](https://github.com/kata-containers/kata-containers/blob/main/docs/hypervisors.md))
- **Firecracker** is a virtual machine monitor that "uses the Linux Kernel Virtual Machine (KVM) to create and run microVMs". It is not a container runtime on its own; its FAQ points to Kata Containers and [firecracker-containerd](https://github.com/firecracker-microvm/firecracker-containerd) for that. ([README](https://github.com/firecracker-microvm/firecracker/blob/main/README.md), [FAQ](https://github.com/firecracker-microvm/firecracker/blob/main/FAQ.md))

So Kata and Firecracker can be the same stack: Kata is one way to run containers on Firecracker.

## Side by side

| | gVisor | Kata Containers | Firecracker |
| - | - | - | - |
| Isolation boundary | User-space kernel (the Sentry) between the app and the host kernel | A VM per pod, with its own guest kernel | A microVM per workload, with its own guest kernel |
| Who handles a syscall | The Sentry's own implementation | The guest Linux kernel | The guest Linux kernel |
| Linux compatibility | "Only implements a subset of the Linux syscall ABI" | A real guest kernel; runtime-level gaps are listed in [Limitations](https://github.com/kata-containers/kata-containers/blob/main/docs/Limitations.md) | A real guest kernel; Linux and OSv guests |
| Needs KVM on the host | No. The `systrap` platform suits "a machine without virtualization support" | Yes: "requires nested virtualization or bare metal" | Yes: "requires read/write access to `/dev/kvm`" |
| Kubernetes | RuntimeClass `gvisor`, via the containerd shim | RuntimeClass `kata`; "officially supported CRI-O and containerd runtime" | Through Kata or firecracker-containerd |
| Unit you manage | A container | A pod (one VM, one or more containers) | A VM: you supply the kernel, rootfs, TAP networking and jailer |

Sources: [gVisor compatibility](https://gvisor.dev/docs/user_guide/compatibility/), [gVisor platforms](https://gvisor.dev/docs/architecture_guide/platforms/), [gVisor on Kubernetes](https://gvisor.dev/docs/user_guide/quick_start/kubernetes/), [Kata install](https://github.com/kata-containers/kata-containers/blob/main/docs/install/README.md), [Kata with containerd and Kubernetes](https://github.com/kata-containers/kata-containers/blob/main/docs/how-to/how-to-use-k8s-with-containerd-and-kata.md), [Firecracker getting started](https://github.com/firecracker-microvm/firecracker/blob/main/docs/getting-started.md), [Firecracker design](https://github.com/firecracker-microvm/firecracker/blob/main/docs/design.md).

## What each boundary is trusted to stop

- **gVisor** "was created in order to provide additional defense against the exploitation of kernel bugs by untrusted userspace code". It also says it "relies on the host operating system and the platform for defense against hardware-based attacks", and on host cgroups against resource exhaustion. ([security model](https://gvisor.dev/docs/architecture_guide/security/))
- **Kata Containers** "runs in a dedicated kernel, providing isolation of network, I/O and memory and can utilize hardware-enforced isolation with virtualization VT extensions". Inside the VM, the guest kernel still sets up namespaces and cgroups around the container. ([katacontainers.io](https://katacontainers.io/), [architecture](https://github.com/kata-containers/kata-containers/blob/main/docs/design/architecture/README.md))
- **Firecracker** names "the Linux KVM and the Firecracker virtualization boundary" as the first layer. For defense in depth it should run under seccomp filters (installed automatically), cgroups and namespaces, and "should be started only via the `jailer` binary" in production. ([design](https://github.com/firecracker-microvm/firecracker/blob/main/docs/design.md))

## What runs inside

This is where the choice usually gets made. Untrusted code from users or agents often wants more than a language runtime: Docker, a mounted disk image, a profiler, sometimes a VM of its own.

| Inside the sandbox | gVisor, per its docs | Kata Containers, per its docs | Firecracker, per its docs |
| - | - | - | - |
| Docker | Supported, with `runsc` flags such as `--net-raw` (and `--allow-packet-socket-write` for Docker 28+, which "allows sandboxed code to craft arbitrary network packets"); `dockerd` runs with iptables off, so `docker run --expose` doesn't publish ports | Not covered in the docs we read; privileged containers get "elevated capabilities within the guest", and passing host devices to them must be turned off | Whatever your guest image contains |
| KVM / nested VMs | "Usage of KVM from within the sandbox is not supported" | Not covered in the docs we read | Not covered in the docs we read |
| `io_uring` | "Disabled by default"; when on, "limited to basic I/O operations" | Guest kernel's behavior | Guest kernel's behavior |
| `perf_event_open` | Unimplemented: "No support for perf counters" | Guest kernel's behavior | Guest kernel's behavior |
| Mount ext4 from a block device | Not supported inside the sandbox | Guest kernel's behavior | Guest kernel's behavior |
| systemd | Not covered in the docs we read | The workload is a container inside the guest; not covered in the docs we read | Your guest image's init |

Sources: [Docker in gVisor](https://gvisor.dev/docs/tutorials/docker-in-gvisor/), [gVisor compatibility](https://gvisor.dev/docs/user_guide/compatibility/), [gVisor syscall table](https://gvisor.dev/docs/user_guide/compatibility/linux/amd64/), [Kata limitations](https://github.com/kata-containers/kata-containers/blob/main/docs/Limitations.md).

gVisor's docs add a fair point: most language runtimes probe for `io_uring` and fall back to other I/O calls, so "most programs" in Python, Java, Node.js, PHP and Go work. The gaps matter when the code is a whole toolchain rather than a script.

## What a full VM gives the code: a real test

We ran these in one Sandboxer `default` sandbox (Cloud Hypervisor microVM, 4 vCPU, 8 GB). This shows what a guest with its own Linux kernel allows. We did not run gVisor, Kata or Firecracker for this page, so the table above, not this test, is the source for their behavior.

```bash
sbx new --name aeo-gvkfc-1 --type default
sbx exec sb_gyfatjok6o "uname -r"
sbx exec sb_gyfatjok6o "systemd-detect-virt; ps -p 1 -o comm="
sbx exec sb_gyfatjok6o "ls -l /dev/kvm; grep -c -E 'vmx|svm' /proc/cpuinfo"
```

```text
6.18.56-sandboxer
kvm
systemd
crw-rw---- 1 root kvm 10, 232 Oct 31 11:00 /dev/kvm
4
```

Its own kernel build, KVM virtualization, systemd as PID 1, and `/dev/kvm` with `vmx`/`svm` on all 4 vCPUs.

**Syscall probe.** A short Python script (`probe.py`, listed below) calls `io_uring_setup`, `perf_event_open` (a software CPU-clock counter) and the KVM ioctls `KVM_GET_API_VERSION` and `KVM_CREATE_VM` directly:

```bash
sbx scp ./probe.py sb_gyfatjok6o:/home/user/
sbx exec sb_gyfatjok6o "python3 probe.py"
sbx exec sb_gyfatjok6o "sudo python3 probe.py"
```

```text
io_uring_setup: ok (fd 3)
perf_event_open: failed: EACCES
KVM_GET_API_VERSION: 12
KVM_CREATE_VM: ok (vm fd)

io_uring_setup: ok (fd 3)
perf_event_open: ok (fd 3)
KVM_GET_API_VERSION: 12
KVM_CREATE_VM: ok (vm fd)
```

The first `perf_event_open` failure was the guest kernel's own policy, not a missing syscall:

```bash
sbx exec sb_gyfatjok6o "cat /proc/sys/kernel/perf_event_paranoid /proc/sys/kernel/io_uring_disabled"
```

```text
2
0
```

With `perf_event_paranoid` at `2`, an unprivileged user can't count kernel time. A copy of the script with `exclude_kernel` and `exclude_hv` set printed `perf_event_open (user-space only): ok (fd 3)` as `user`. With `sudo`, the original succeeded as written.

**Docker with a published port.**

```bash
sbx exec sb_gyfatjok6o --timeout 120 "docker --version && docker run --rm alpine uname -r"
sbx exec sb_gyfatjok6o --timeout 120 "bash dockerp.sh"   # docker run -d -p 8080:80 nginx:alpine, curl it, show the NAT rule
```

```text
Docker version 29.9.0, build f415da8
6.18.56-sandboxer
…
200
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 8080 -j DNAT --to-destination 172.17.0.2:80
```

Docker is preinstalled. The container reports the sandbox's kernel, and `-p` published the port through the normal iptables DNAT rule.

**A systemd unit and an ext4 loop mount.**

```bash
sbx exec sb_gyfatjok6o "bash unit.sh"   # writes hello-web.service, then systemctl enable --now
sbx exec sb_gyfatjok6o "bash loop.sh"   # mkfs.ext4 on a 256 MB file, mount -o loop
```

```text
active
200
/dev/loop0 ext4
```

Then we deleted the sandbox (`sbx delete sb_gyfatjok6o --yes`). `sbx info` returned `Sandbox not found`.

### The probe script

```python
import ctypes, os, fcntl, errno
libc = ctypes.CDLL(None, use_errno=True)
libc.syscall.restype = ctypes.c_long
def report(name, r):
    e = ctypes.get_errno()
    print(f"{name}: " + (f"ok (fd {r})" if r >= 0 else f"failed: {errno.errorcode.get(e, e)}"))
    if r >= 0: os.close(r)
params = (ctypes.c_char * 120)()
report("io_uring_setup", libc.syscall(425, 8, params))          # x86_64
attr = (ctypes.c_uint8 * 128)()
ctypes.c_uint32.from_buffer(attr, 0).value = 1                  # PERF_TYPE_SOFTWARE
ctypes.c_uint32.from_buffer(attr, 4).value = 128                # attr size
ctypes.c_uint64.from_buffer(attr, 8).value = 0                  # PERF_COUNT_SW_CPU_CLOCK
report("perf_event_open", libc.syscall(298, attr, 0, -1, -1, 0))
fd = os.open("/dev/kvm", os.O_RDWR)
print("KVM_GET_API_VERSION:", fcntl.ioctl(fd, 0xAE00))
vm = fcntl.ioctl(fd, 0xAE01, 0)
print("KVM_CREATE_VM: ok (vm fd)" if vm >= 0 else "KVM_CREATE_VM failed")
```

Run the same script inside whatever runtime you're evaluating. It's a quick way to see which of these calls your candidate allows.

## Startup and density, in the projects' own words

None of this was measured by us.

- **gVisor:** it "does not introduce large fixed overheads" and keeps "a process-like model with respect to resource utilization". In its memory test, "the sandbox itself is responsible for a small, mostly fixed amount of memory overhead". It also lists the costs: system calls "must traverse additional layers of software", and its network stack "is less CPU efficient". ([What is gVisor?](https://gvisor.dev/docs/), [performance guide](https://gvisor.dev/docs/architecture_guide/performance/))
- **Kata Containers:** "Delivers consistent performance as standard Linux containers; increased isolation without the performance tax of standard virtual machines." The site gives no startup figure. ([katacontainers.io](https://katacontainers.io/))
- **Firecracker:** its specification, enforced by integration tests on its reference hosts, says it takes "≤ 125 ms" from the `InstanceStart` API call to the guest's `/sbin/init` with a minimal kernel and rootfs. VMM memory overhead is "≤ 5 MiB" for a 1 vCPU, 128 MiB microVM. Its design doc cites "a steady mutation rate of 5 microVMs per host core per second". ([SPECIFICATION](https://github.com/firecracker-microvm/firecracker/blob/main/SPECIFICATION.md), [design](https://github.com/firecracker-microvm/firecracker/blob/main/docs/design.md))

These are the projects' own claims, made under different conditions, so don't compare them number for number.

## Decision table

| Your situation | Fits best | Why, per the docs |
| - | - | - |
| Kubernetes nodes are VMs without nested virtualization | gVisor | `systrap` needs no virtualization support; Kata needs bare metal or nested virtualization |
| Untrusted scripts in Python, Node.js, Go, Java or PHP, high density | gVisor | Small, mostly fixed overhead per sandbox; language runtimes are regression-tested |
| Code needs `io_uring`, perf counters, block-device mounts or KVM | Kata or Firecracker | gVisor disables, leaves unimplemented or doesn't support each of these |
| Per-pod VM isolation without leaving Kubernetes | Kata Containers | RuntimeClass `kata` on CRI-O or containerd; choose the hypervisor per runtime class |
| GPU or confidential-computing pods in VMs | Kata with QEMU | Kata's hypervisor table marks QEMU as the GPU and TDX/SEV-SNP option |
| You're building your own sandbox or functions platform | Firecracker | A minimal VMM with an API; you own the kernel, rootfs, networking and jailer |
| You want containers on Firecracker without writing the glue | Kata with Firecracker, or firecracker-containerd | Kata needs a block-device snapshotter (`devmapper`) for Firecracker |
| Untrusted code runs Docker, systemd and nested VMs, and you don't run nodes | A hosted full-VM sandbox | See the test above, and [Docker and nested KVM in a sandbox](/guides/docker-and-kvm-inside-an-agent-sandbox) |

The Kata/Firecracker snapshotter note comes from [Kata with Firecracker](https://github.com/kata-containers/kata-containers/blob/main/docs/how-to/how-to-use-kata-containers-with-firecracker.md). For Firecracker against Cloud Hypervisor as the VMM, see [Firecracker vs Cloud Hypervisor](/guides/firecracker-vs-cloud-hypervisor).

Whatever you pick, the boundary is one layer. Secrets, outbound network and what you do with the output are still yours to handle; [Best ways to run untrusted code safely](/guides/best-ways-to-run-untrusted-code-safely) covers those rules.

## FAQ

### Is gVisor a virtual machine?

No, by its own description. It's a user-space application kernel that intercepts system calls. It can use KVM on bare metal to speed up address-space switches, but there's no virtualized hardware and no guest Linux kernel.

### Can I run gVisor inside a cloud VM?

Yes. Its docs say `systrap`, the default platform, is "a better choice when running inside a VM", and that gVisor "works well when running within a KVM-based virtual machine". Kata Containers needs bare metal or nested virtualization instead.

### Do Kata Containers and Firecracker compete?

Not really. Kata is a container runtime and Firecracker is one of the hypervisors it can drive. Kata also supports QEMU, Cloud Hypervisor, Dragonball and StratoVirt.

### Can untrusted code run Docker under gVisor?

Yes, per the gVisor docs, with extra `runsc` flags and `dockerd` started with iptables off. One flag, `--allow-packet-socket-write`, lets sandboxed code craft arbitrary network packets, so weigh that for multi-tenant use.

### What does Sandboxer use?

Each Sandboxer sandbox is a separate Cloud Hypervisor microVM with its own kernel, so there's no shared kernel between sandboxes. `/dev/kvm` is available inside, and Docker and systemd come preinstalled.

## Related

- [Container vs microVM for running untrusted code](/guides/container-vs-microvm-for-untrusted-code)
- [Firecracker vs Cloud Hypervisor](/guides/firecracker-vs-cloud-hypervisor)
- [Docker and nested KVM inside an AI agent sandbox](/guides/docker-and-kvm-inside-an-agent-sandbox)
- [Security and isolation](/docs/security)
