The core idea
A virtual machine pretends to be a whole computer. It gets virtual CPUs, memory and disks, and boots its own operating system with its own kernel.
A container doesn't pretend to be a computer at all. It's a normal process running on the host's kernel, which has been told to show that process a private view of the system.
Everything else in this lesson follows from that.
What follows from sharing a kernel
| Virtual machine | Container | |
|---|---|---|
| Starts in | tens of seconds (it boots an OS) | about as long as the app takes to start |
| Image size | gigabytes | often tens to hundreds of megabytes |
| Runs a different OS | yes, Windows on a Linux host is fine | only one built for the host's kernel |
| Isolation boundary | the hypervisor, a small, hardened layer | the shared kernel, a much larger surface |
| Overhead per instance | a whole OS's memory | close to a normal process |
The speed and size come for free: there's no second OS to boot or store. The trade-off is the isolation boundary. Every container talks to the same kernel, so a kernel vulnerability can let a process escape its container. A VM attacker has to break the hypervisor instead, which is far harder.
That's why platforms that run strangers' code side by side often wrap containers in very light VMs. AWS Lambda and Fargate, for example, run on Firecracker microVMs, and tools like gVisor and Kata Containers exist for the same reason.
The Mac and Windows question
If containers need the host's kernel, how does a Linux image run on a Mac?
It doesn't, directly. Docker Desktop on macOS and Windows starts a small Linux VM in the background, and your containers run on that VM's kernel. It's mostly invisible, but it explains things you'll hit later: file sharing between your Mac and containers is slower than on Linux, and the memory you give Docker Desktop is the VM's memory.
On an Ubuntu 24.04 server, you start a container from a Fedora image and run uname -r inside it. Whose kernel version is printed?
See it yourself
Compare the kernel and the OS release, on your machine and inside a container.
$ uname -r25.6.0$ docker run --rm alpine uname -r6.12.54-linuxkit$ docker run --rm alpine cat /etc/os-release | head -2NAME="Alpine Linux"ID=alpine
Output from a Mac with Docker Desktop. The Mac reports its own kernel (25.6.0); the container reports the hidden Linux VM's kernel (linuxkit). On a Linux host, the two versions would match exactly. Either way, the OS release says Alpine, because that's only the userland in the image.
STUCK? ▸
On Linux, the two uname -r lines should be identical. On a Mac or Windows machine they differ, and the container's has linuxkit or microsoft in it: that's the Linux VM from the section above.
Recap
Interview questions
What is the difference between a container and a virtual machine?
Can you run a Windows container on a Linux host? Why or why not?