# Docker vs virtual machine: which should run untrusted code? A Docker container is an isolated process that shares the host's kernel; a virtual machine runs its own kernel behind a hypervisor. **Runtime gives you both at once: every sandbox is a Firecracker virtual machine, and Docker runs inside it after `sudo enable-docker`.** A new sandbox ran its first Python command 351 ms after the create request at the median, over the public internet, on 24 September 2026 ([speed](/docs/speed)), so a fresh VM per job costs little time. ## What Docker's own documentation says Docker's introduction draws the line plainly. "A VM is an entire operating system with its own kernel, hardware drivers, programs, and applications. Spinning up a VM only to isolate a single application is a lot of overhead. A container is simply an isolated process with all of the files it needs to run. If you run multiple containers, they all share the same kernel." That shared kernel is the trade. Containers are light because they are processes. A VM is heavier because it brings a kernel, and that kernel is the reason code inside it cannot reach the host's. ## How each one isolates **A Docker container** is fenced by the host kernel. Docker's security guide says `docker run` creates "a set of namespaces and control groups for the container," and that namespaces "provide the first and most straightforward form of isolation." On top, Docker starts containers with a reduced set of Linux capabilities and a default seccomp profile that "disables around 44 system calls out of 300+." The daemon behind it "requires root privileges unless you opt-in to Rootless mode." **A virtual machine** is fenced by the processor. A hypervisor such as KVM gives the guest virtual CPUs and memory, and a monitor such as QEMU or Firecracker gives it virtual devices. The guest's system calls go to its own kernel. To reach the host, code must first break that kernel and then the monitor. ## Side by side | | Docker container | Virtual machine | | -------------------------- | ---------------------------------------------------- | ------------------------------------------------- | | What it is | "An isolated process with all of the files it needs" | "An entire operating system with its own kernel" | | Kernel | The host's, shared by every container | Its own | | Boundary | Namespaces, cgroups, capabilities, seccomp | Hardware virtualization and the monitor's devices | | Root inside | Root on the shared kernel, limited by capabilities | Root in its own machine only | | Other operating systems | Linux containers need a Linux kernel | Any guest the hypervisor supports | | Overhead | A process | A kernel and its memory; microVMs keep this small | | Docker's rating for agents | "Partial (namespaces)", for trusted tools | "Full (hypervisor)", for autonomous agents | The last row is Docker's own. Its Docker Sandboxes product runs coding agents in microVMs, and its architecture guide compares the options: sandboxes in microVMs get "Full (hypervisor)" isolation for "Autonomous agents", a container with the Docker socket mounted gets "Partial (namespaces)" for "Trusted tools", and Docker-in-Docker gets "Partial (privileged)" for CI pipelines. Docker's summary: sandboxes "trade higher resource overhead (a VM plus its own daemon) for complete isolation." ## Is Docker enough for untrusted code? For code you wrote and trust, a container is a fine package. For code a model wrote, the question is what happens when it finds a kernel bug, and in a container the answer is that it is running on your kernel. The Docker daemon is a second risk. Docker warns that "only trusted users should be allowed to control your Docker daemon," because a container can be started with the host's `/` mounted and "can alter your host filesystem without any restriction." An agent that can call `docker run` on your host, through a mounted socket or otherwise, can do the same. Docker itself reaches for a VM when it needs one. Docker Desktop on Linux "runs a Virtual Machine (VM)", partly because "Docker controls the kernel and the OS inside the VM." ## Use both: Docker inside a VM The two are layers, not rivals. Docker's getting-started guide notes that in the cloud "the provisioned machines are typically VMs" that run many containers. For agent code the useful layering is one VM per job, with Docker inside it if the job needs containers. The agent gets the whole Docker workflow, `docker build`, `docker compose` and published ports, and the daemon it controls belongs to its own disposable machine. ## When each fits - **Pick a container** for your own services, builds and tests: code you trust, packaged the same way from laptop to production. - **Pick a virtual machine** for code you did not write: model output, user submissions, agents with a shell, or any job that needs root or its own Docker daemon. - **Pick a microVM** when you need that VM boundary for many short jobs and a full VM's start time and memory would be too slow or too costly. ## Docker in a Runtime sandbox Each Runtime sandbox is a Firecracker microVM with its own kernel. Docker Engine, Buildx and Compose install on first use, containers reach the web through the sandbox's proxy, and the network rules set on the host bind them too ([Docker](/docs/sandbox-environment#docker)). ```ts check import { Sandbox } from "withruntime"; await using sbx = await Sandbox.create({ diskMiB: 8192, timeoutSeconds: 900 }); await sbx.exec("sudo enable-docker", { check: true, timeoutMs: 300_000 }); const run = await sbx.exec("docker run --rm hello-world", { timeoutMs: 120_000 }); console.log(run.stdout); ``` ```python check from withruntime import Sandbox with Sandbox.create(disk_mib=8192, timeout_seconds=900) as sbx: sbx.exec("sudo enable-docker", check=True, timeout_ms=300_000) run = sbx.exec("docker run --rm hello-world", timeout_ms=120_000) print(run.stdout) ``` Root in the sandbox, and root in its containers, cannot change the network rules, CPU, memory or cost, which are enforced outside the microVM ([security](/docs/security)). Any Dockerfile or container image also builds into a sandbox image, so a sandbox can start with your stack already installed ([images](/docs/images)). New accounts get 50 free sandbox hours, no card: ```bash no-run npx withruntime sandbox run --trial -- uname -r ``` More: [run Docker in a sandbox](/how-to/run-docker-in-a-sandbox), [Firecracker vs Docker](/compare/firecracker-vs-docker), [microVM vs container](/compare/microvm-vs-container), [gVisor vs Docker](/compare/gvisor-vs-docker). ## Sources Checked 25 September 2026. - Docker: [what is a container?](https://docs.docker.com/get-started/docker-concepts/the-basics/what-is-a-container/), [Docker Engine security](https://docs.docker.com/engine/security/), [seccomp profiles](https://docs.docker.com/engine/security/seccomp/), [rootless mode](https://docs.docker.com/engine/security/rootless/) - Docker: [Docker Sandboxes](https://docs.docker.com/ai/sandboxes/), [Sandboxes architecture](https://docs.docker.com/ai/sandboxes/architecture/), [Docker Desktop for Linux](https://docs.docker.com/desktop/setup/install/linux/), [Linux FAQs](https://docs.docker.com/desktop/troubleshoot-and-support/faqs/linuxfaqs/) - [Firecracker](https://firecracker-microvm.github.io/) Facts on this page were checked on 25 September 2026.