Containers vs Virtual Machines
Containers and virtual machines both let you run an app in its own isolated space. They look similar from the outside, so beginners often treat them as the same thing. They are built very differently, and that difference decides how fast they start, how much memory they use and how safe they are.
In this lesson you will compare the two, see the Linux features that make containers possible, and understand what Docker actually runs on your Windows or Mac laptop.
Virtual machines: a whole computer in software
A virtual machine (VM) pretends to be a complete computer. A program called a hypervisor (VirtualBox, VMware, Hyper-V or KVM) creates fake hardware: a CPU, memory, a disk and a network card. On that fake hardware you install a full guest operating system, with its own kernel, its own drivers and its own system services. Your app runs on top of that.
The kernel is the core of an operating system. It talks to the hardware, manages memory and decides which program uses the CPU next. Every VM has its own kernel, which is why you can run Windows inside a VM on a Linux server.
This gives very strong isolation, but it costs a lot. A VM for a small web app might need 1 GB of memory just for the guest operating system, several gigabytes of disk and 30 seconds or more to boot.
Containers: isolated processes on a shared kernel
A container does not pretend to be a computer. It is an ordinary process (or a small group of processes) running on the host's kernel. The kernel simply gives it a restricted view of the system. Two Linux kernel features do the work:
- Namespaces control what a process can see. With its own namespaces, a container sees only its own processes, its own network interfaces, its own host name and its own file system.
- Control groups (cgroups) control what a process can use: how much memory, how much CPU time and how many processes.
Because there is no guest kernel to boot, a container starts in well under a second and uses only the memory your app needs. A laptop with 8 GB of memory can run a few VMs but dozens of containers.
| Virtual machine | Container | |
|---|---|---|
| What is isolated | A whole computer with its own kernel | A process with its own view of the system |
| Start-up time | Tens of seconds | Usually under a second |
| Size on disk | Gigabytes | Megabytes to a few hundred megabytes |
| Memory overhead | A full guest OS for each VM | Almost none beyond the app |
| Can run a different OS kernel | Yes (Windows on Linux, for example) | No, it uses the host kernel |
| Isolation strength | Very strong | Strong, but the kernel is shared |
Seeing the shared kernel for yourself
You can prove that a container uses the host's kernel. On a Linux server, compare the kernel version on the host with the one inside a container. The --rm option deletes the container as soon as the command finishes, and alpine:3.22 is a tiny Linux distribution image.
uname -r
docker run --rm alpine:3.22 uname -r6.8.0-87-generic 6.8.0-87-generic
The same kernel answers both times. Now ask which Linux distribution each one thinks it is:
grep PRETTY_NAME /etc/os-release
docker run --rm alpine:3.22 grep PRETTY_NAME /etc/os-releasePRETTY_NAME="Ubuntu 24.04.3 LTS" PRETTY_NAME="Alpine Linux v3.22"
The host is Ubuntu, but the container has Alpine's files, Alpine's package manager and Alpine's tools. A container image brings its own user space (files and programs) but borrows the host's kernel. That is why a container built on Alpine runs happily on an Ubuntu, Debian or Fedora server.
The process namespace is just as easy to see. Inside a container, your command is process number 1 and there is nothing else:
docker run --rm alpine:3.22 psPID USER TIME COMMAND
1 root 0:00 psOn the host, ps would list hundreds of processes. The container cannot see any of them.
Limiting resources with cgroups
Control groups stop one container from eating all the memory or CPU on a server. You set limits when you start the container:
docker run -d --name report-job --memory 256m --cpus 0.5 alpine:3.22 sleep 600This container may use at most 256 MB of memory and half of one CPU core. If the app inside tries to use more memory, the kernel stops it (you will see exit code 137 in Debugging & Troubleshooting Containers). Without limits, a container may use everything the host has, which is the default.
What runs on Windows and macOS?
Containers need a Linux kernel, but Windows and macOS do not have one. So Docker Desktop quietly runs a small, fast Linux virtual machine and runs your containers inside it:
- On Windows, Docker Desktop uses WSL 2 (Windows Subsystem for Linux), which runs a real Linux kernel in a lightweight VM managed by Windows.
- On macOS, Docker Desktop uses Apple's virtualization framework to run its Linux VM.
- On a Linux server you install Docker Engine directly, with no VM at all. This is what production servers use.
You do not have to manage this VM yourself. The docker command on your laptop talks to the Docker Engine inside it, and published ports such as localhost:8080 are forwarded for you.
When to choose which
Containers and VMs are not enemies. Most cloud servers are themselves VMs, and you run containers inside them.
- Use containers to package and run apps: web servers, APIs, databases for development, background workers and build tools.
- Use VMs when you need a different kernel or operating system, very strong isolation between customers, or a full desktop environment to test something.
Common mistakes
- Expecting a container to boot like a VM. A container has no boot process and usually no system services such as
systemd. It runs one main program; when that program exits, the container stops. - Trying to run a Windows app in a Linux container. The container uses the host's Linux kernel, so it can only run Linux programs.
- Forgetting resource limits on shared servers. One runaway container without
--memorycan slow every other app on the machine.
Key points
- A VM emulates a whole computer and runs its own guest kernel; a container is an isolated process on the host's kernel.
- Linux namespaces limit what a container can see; cgroups limit what it can use.
- Containers start faster and use far less memory and disk than VMs.
- An image brings its own files (user space) but shares the host kernel, so an Alpine container runs on an Ubuntu host.
- Docker Desktop on Windows and macOS runs a small Linux VM; on Linux servers Docker Engine runs directly.
Exercise
On a computer with Docker, run a container from alpine:3.22 with a limit of 128 MB of memory that prints its host name with the hostname command. Then run the same command without Docker on your own machine and compare the two names. Explain in one sentence why they are different.
Show solution
docker run --rm --memory 128m alpine:3.22 hostname
hostname5d1e8a0c9b27 aarav-laptop
The container has its own UTS namespace (the namespace for the host name), so Docker gives it a name based on its container ID instead of your computer's name. Your container ID will be different.
Quick Quiz
1. Which Linux feature limits how much memory a container can use?
cgroups control resource use; namespaces control what a process can see.
2. Why can an Alpine Linux container run on an Ubuntu server?
All Linux distributions share the same kind of kernel, so only the user-space files differ.
3. How does Docker Desktop run Linux containers on Windows?
Containers need a Linux kernel, which WSL 2 provides.
4. Which statement about VMs is true?
Each VM has its own guest kernel, which is why it is heavier but more flexible.
Learn Docker & DevOps with a mentor
Free lessons get you started. Our Full Stack Web Development training adds real projects and code reviews from working developers.
- Project-based learning
- Mentor code reviews
- Free counselling call
