# Container vs microVM for running untrusted code

> Use a microVM when the code is untrusted. A container is a set of Linux namespaces and cgroups on the host's own kernel, so a kernel bug or a mounted Docker socket can let code out. A microVM gives the code its own kernel behind hardware virtualization, which is a much smaller boundary to attack. gVisor sits in between.

Source: https://sandboxer.com/guides/container-vs-microvm-for-untrusted-code. Updated 2026-10-24.

**Short answer:** Use a microVM when the code is untrusted. A container is a set of Linux namespaces and cgroups on the host's own kernel, so a kernel bug or a mounted Docker socket can let code out. A microVM gives the code its own kernel behind hardware virtualization, which is a much smaller boundary to attack. gVisor sits in between.

## The three boundaries

| | Container | gVisor | MicroVM |
| - | - | - | - |
| What isolates the code | Linux namespaces and cgroups | A user-space "application kernel" (the Sentry) that intercepts system calls | Hardware virtualization (KVM) plus the VMM |
| Kernel | Shared with the host | gVisor's own, in user space, on top of the host kernel | The guest's own kernel |
| Typical runtime | runc (Docker, containerd) | runsc | Firecracker, Cloud Hypervisor, or Kata Containers on top of them |
| Compatibility | Full Linux | Most of the Linux API; some calls and tools behave differently | Full Linux, including Docker and systemd inside |
| Overhead | Lowest | Low to moderate | A VM per sandbox |

## What the projects themselves say

- **Docker:** "When you start a container with docker run, behind the scenes Docker creates a set of namespaces and control groups for the container." The Docker daemon "requires root privileges unless you opt-in to Rootless mode", and "the docker group grants root-level privileges to the user." ([Docker security](https://docs.docker.com/engine/security/), [post-install](https://docs.docker.com/engine/install/linux-postinstall/))
- **Docker, on its own agent sandboxes:** "Unlike containers, which share the host kernel, a sandbox VM cannot access host processes, files, or resources outside its defined boundaries." Docker's architecture table rates a container with a socket mount as "Partial (namespaces)" isolation for "Trusted tools", and microVM sandboxes as "Full (hypervisor)" for "Autonomous agents". ([isolation](https://docs.docker.com/ai/sandboxes/security/isolation/), [architecture](https://docs.docker.com/ai/sandboxes/architecture/))
- **gVisor:** "gVisor was created in order to provide additional defense against the exploitation of kernel bugs by untrusted userspace code." On hardware-level attacks it notes that, even when hardware virtualization is used, "the host kernel or hypervisor is ultimately responsible for defending against attacks from within malicious guests." ([gVisor security](https://gvisor.dev/docs/architecture_guide/security/))
- **Kata Containers:** "lightweight virtual machines that feel and perform like containers, but provide stronger workload isolation using hardware virtualization technology." ([katacontainers.io](https://katacontainers.io/))
- **Firecracker:** "Customer workloads are simultaneously considered sacred (shall not be touched) and malicious (shall be defended against)." Its docs also say "the overall security of Firecracker microVMs ... depends on a well configured Linux host operating system", and that "all egress traffic from a guest is ... considered untrusted, and should be filtered at the host-level." ([charter](https://github.com/firecracker-microvm/firecracker/blob/main/CHARTER.md), [README](https://github.com/firecracker-microvm/firecracker/blob/main/README.md), [design](https://github.com/firecracker-microvm/firecracker/blob/main/docs/design.md))

## Common ways a container sandbox leaks

1. **Mounting `/var/run/docker.sock`.** Whoever controls the daemon controls the host; Docker says the `docker` group is root-equivalent.
2. **`--privileged` for Docker-in-Docker.** Gives up most of the container's isolation to make nested Docker work.
3. **Kernel bugs.** Every container on a host talks to the same kernel; one exploitable bug is a way out for all of them.
4. **Shared host resources.** Host paths, host networking or a shared credentials file mounted in for convenience.

A microVM changes the first two from "dangerous" to "normal": the code gets its own Docker daemon and root inside its own VM. The third becomes a guest-kernel bug, which stays inside the VM unless it can also break the hypervisor.

## A microVM doesn't make everything safe

| Still your job | Why |
| - | - |
| Secrets you put inside | Code in the VM can read and send them |
| Outbound network | A misled agent can call out; filter egress or keep secrets short-lived |
| What you do with the output | Review code before you merge or deploy it |
| Snapshot clones | Firecracker's [snapshot docs](https://github.com/firecracker-microvm/firecracker/blob/main/docs/snapshotting/snapshot-support.md) warn that identifiers, random seeds and tokens are copied into every VM restored from one snapshot |

## Running untrusted code in a microVM on Sandboxer

Each Sandboxer sandbox is a Cloud Hypervisor microVM with its own kernel. Sandboxes can't reach each other or our internal networks. Start one with none of your secrets, run the code, and delete it:

```bash
sbx new --no-env
sbx scp ./untrusted.py sb_k3v9x2mq:/home/user/
sbx exec sb_k3v9x2mq --timeout 300 "python3 untrusted.py"
sbx delete sb_k3v9x2mq --yes
```

For per-user sandboxes in your own product, create each one with `noEnv` through the API, so it never holds your credentials.

## FAQ

### Is a Docker container secure enough for untrusted code?

A plain container isolates with namespaces and cgroups on the shared host kernel; Docker's own architecture table rates the common agent setup, a container with the Docker socket mounted, as partial isolation for trusted tools, and microVMs as full isolation for autonomous agents. For code you don't trust, use a VM boundary.

### What is the difference between gVisor and a microVM?

gVisor runs a user-space kernel that intercepts the application's system calls, so the host kernel sees far fewer of them. A microVM gives the code a real guest kernel behind hardware virtualization. gVisor is lighter; a microVM is fully Linux-compatible, including Docker and systemd inside.

### Are microVMs slower than containers?

A microVM boots a kernel and has more memory overhead than a container. Firecracker documents boot to `/sbin/init` in `<= 125 ms` and `<= 5 MiB` of VMM overhead under its stated test conditions ([specification](https://github.com/firecracker-microvm/firecracker/blob/main/SPECIFICATION.md)). For agent workloads that run for minutes or hours, the startup difference rarely matters.

### What is Kata Containers?

A container runtime that runs each container or pod in a lightweight VM, using Cloud Hypervisor, Firecracker, QEMU or others. You keep the container workflow and get a VM boundary.

### Can code escape a microVM?

Mainly through a bug in the VMM or the host's KVM, through hardware side channels, or through a badly configured host. That is a much smaller target than the full Linux system-call interface a container shares. Firecracker and Cloud Hypervisor both add seccomp filters on the VMM process as defense in depth.
