Theory Tutorial
Containers vs. Virtual Machines
- Time
- 10m
- Level
- not specified
- Artifacts
- theory + practice
Containers vs. Virtual Machines
Design claim: Containers and virtual machines isolate at different boundaries, so their similarities must not hide their different trust and lifecycle models.
Starting model
- You can explain how one packaged application release crosses environments through a container-engine contract.
- The useful vocabulary at this point is deliberately small: container, virtual machine, shared kernel, guest kernel.
Choose the boundary, not the label
A platform must choose isolation strong enough for the workload without paying for a separate operating system when process isolation is sufficient. Begin with the guarantee visible to a workload or operator. Kernel machinery matters only after a simpler arrangement can no longer supply that guarantee.
The tempting shortcut is straightforward: treat a container as a smaller virtual machine. The shortcut is plausible on a quiet development host, where its missing boundary has not yet been tested. Containers share the host kernel, while virtual machines interpose a hypervisor and boot a guest kernel, which changes startup, density, and failure containment. The constraint reveals which responsibility must move before the design can survive isolation or process failure.
Shared kernel and guest kernel are different contracts
Isolation always means drawing a line and lying to whatever is above it. A hypervisor draws the line at the hardware: each guest gets fake CPUs, fake disks, fake network cards, and must boot its own kernel to drive them. A container engine draws the line at the kernel's system-call interface: every container shares the one real host kernel, but the kernel lies to each process tree about what it can see — its own process list, its own filesystem root, its own hostname. Nothing boots; a container "starting" is just a normal process starting inside a set of kernel-enforced lies.
A container constrains a host process with kernel isolation, whereas a virtual machine virtualizes hardware for an independently booted operating system. The named mechanism is the consequence of that separation: one layer owns policy, another enforces it, and callers receive a stable promise.
Read the topology as a map of authority. Durable knowledge, transient setup, and kernel enforcement should not blur into one box.
flowchart TB
subgraph VM["Virtual machines"]
direction TB
A1["App A"] --> G1["Guest OS + kernel A"]
A2["App B"] --> G2["Guest OS + kernel B"]
G1 --> HV["Hypervisor ⟵ isolation line"]
G2 --> HV
HV --> HW1["Hardware"]
end
subgraph CT["Containers"]
direction TB
B1["App A + libs"] --> K["Shared host kernel ⟵ isolation line"]
B2["App B + libs"] --> K
ENG["Container engine"] -. configures .-> K
K --> HW2["Hardware"]
end
It contrasts processes sharing a host kernel with guests owning separate kernels above a hypervisor. Its central claim is that the chosen boundary must match the trust boundary rather than the preferred packaging format; the labels therefore describe authority rather than decorative grouping.
Startup reveals what is actually isolated
Because a VM must initialize virtual hardware and boot a kernel, startup is measured in seconds to minutes and each guest costs gigabytes of memory before the app runs. A container skips all of it — the kernel is already running. The trade: containers offer a weaker boundary (one shared kernel means one kernel bug can cross the line), which is why Part 1 ends with a security lesson and Lesson 39 hardens it further.
Container startup prepares isolated views and executes a process; virtual-machine startup creates virtual hardware and boots a kernel before the application can run. Trace the order carefully; each step establishes a fact the next step is entitled to use.
The second figure tests the same model in motion: it makes the shorter process-start path and longer guest-boot path explicit.
sequenceDiagram
participant HV as Hypervisor
participant G as Guest OS
participant E as Container engine
participant K as Host kernel
rect rgb(253, 244, 235)
note over HV,G: VM path — seconds to minutes
HV->>G: allocate vCPU, vRAM, vDisk
G->>G: boot kernel, mount fs, run init
G->>G: finally, start the app
end
rect rgb(235, 245, 253)
note over E,K: Container path — milliseconds
E->>K: create isolated view (namespaces)
E->>K: apply resource limits (cgroups)
E->>K: exec the app process
end
It makes the shorter process-start path and longer guest-boot path explicit. The ordering is valid only when it continues to preserve the stated invariant under retries and interruption.
Density does not erase the trust boundary
A kernel defect or hostile system call crosses the boundary that containers share but remains outside a guest kernel boundary. A sound boundary either preserves its promise or refuses the operation where the promise becomes impossible.
The chosen boundary must match the trust boundary rather than the preferred packaging format. This rule survives changes in implementation names because it describes ownership rather than a particular process tree.
MiniDock stays honest about its boundary
MiniDock will construct process isolation and must not claim the properties of hardware virtualization. MiniDock needs the property now, but its learner must still decide where the responsibility belongs.
What carries forward
- The chosen boundary must match the trust boundary rather than the preferred packaging format.
- Choose whether a workload boundary needs process isolation or a separate guest operating system.
- The rejected shortcut remains a diagnostic: if the design starts depending on it again, the original constraint has probably been lost.
See what actually stuck.
Take the practice scenarios now.