Virtualization and containerization
Virtualization creates "virtual" resources from real hardware to use the physical machine fully. There are several kinds: server, desktop (user workstations served from a central server), OS, data, network functions (NFV).
Containerization packages code with only the libraries and dependencies it needs into a lightweight container that runs as an isolated process.
flowchart TB
subgraph VM["Virtual machines"]
direction TB
A1["App A"] --> G1["Guest OS"]
A2["App B"] --> G2["Guest OS"]
G1 --> H["Hypervisor"]
G2 --> H
H --> HO1["Host OS / Server"]
end
subgraph CT["Containers"]
direction TB
C1["App A"] --> E["Docker Engine"]
C2["App B"] --> E
E --> HO2["Host OS kernel / Server"]
end
How they differ
| VM | Container | |
|---|---|---|
| OS | A full guest OS per VM | Shares the host kernel |
| Startup | Boots an OS, takes minutes | Starts a process, takes seconds |
| Overhead | GBs of RAM and disk per VM | MBs; containers share layers |
| Use when | You need another kernel/OS (Windows on Linux) | You package and replicate apps quickly |
A container has no kernel of its own: run uname -r inside a container and you see the host kernel.
Copy-on-write
An image is a stack of read-only layers. Each container only adds a thin writable layer on top; when it changes a file, Docker copies the file up to that layer first and then writes. So 10 containers from one image still share the layers underneath, and updating an image only pulls the layers that changed.
What the kernel provides
Containers are built from ordinary Linux kernel features:
| Feature | What it isolates or limits |
|---|---|
| Namespaces | What a process can see: pid (process tree), net (interfaces, ports), mnt (filesystem), uts (hostname), ipc, user (UID mapping) |
| cgroups | How much it can use: CPU, memory, I/O, number of processes |
| Union filesystem (overlayfs) | Stacks image layers plus a writable layer into one view |
| Capabilities, seccomp, AppArmor/SELinux | Which privileged operations and system calls are allowed |
A container is therefore just a process (or a few) with a restricted view of the machine. That is why it starts in milliseconds and why ps on the host shows container processes.
Standards and other tools
The Open Container Initiative (OCI) defines an image format and a runtime specification, so an image built with Docker runs under other tools too. Common runtimes and engines:
- containerd and runc: the pieces Docker itself uses underneath; Kubernetes talks to containerd or CRI-O directly.
- Podman: a Docker-compatible CLI without a central daemon, able to run rootless.
- Kata Containers and Firecracker microVMs: give each container or function its own small VM kernel when stronger isolation is needed.
Security trade-off
Because containers share the kernel, a kernel vulnerability or a container running as root with too many privileges can affect the host. Good habits: run as a non-root user, avoid --privileged, drop unneeded capabilities, and keep the host kernel patched.
They work together
In practice the two are combined: cloud providers run your containers on VMs, and Kubernetes nodes are usually VMs. VMs give hard isolation between tenants and different operating systems; containers give fast, dense packaging of applications on top. Windows containers also exist, but they need a Windows host kernel.