Two answers to one question
This whole rung has chased a single wish: let many independent workloads each behave as if it owned a machine, on one physical box. You saw the first answer early on — a virtual machine, a complete fake computer conjured by a hypervisor, running its own full guest operating system from its own kernel upward. Then you met a second, lighter answer — a container, which does not fake the hardware at all but instead carves the one real OS into many private-looking slices. Both deliver the same illusion of 'this is my machine'. They build it in completely different places, and that difference is the whole story.
Here is the cleanest way to hold the contrast in your head. A virtual machine duplicates the OS: every VM brings its own kernel, its own paging tables, its own everything, and the hypervisor draws a hard line under all of them. A container shares the OS: all the containers on a host run on the single host kernel, and that kernel quietly hands each one its own private view of the system. Duplicating buys strength; sharing buys lightness. You cannot have the maximum of both at once, and pretending otherwise is the single most common confusion in this corner of the field.
VIRTUAL MACHINES CONTAINERS ---------------- ---------- [ App A ] [ App B ] [ App A ] [ App B ] [ App C ] [Guest OS][Guest OS] --------------------------------- [ kernel ][ kernel ] | namespaces + cgroups (views) | -------------------- --------------------------------- | HYPERVISOR | | ONE shared host kernel | -------------------- --------------------------------- | hardware | | hardware | -------------------- --------------------------------- Each VM = its own kernel. All containers = the SAME kernel.
Weight: what each one carries up the stairs
Think of weight as everything you must haul into memory and boot before your actual program runs. A virtual machine has to carry an entire guest OS up the stairs: its kernel, its drivers, its background services, its own page tables. Booting it is booting a whole computer, which takes seconds to minutes and a hefty slice of RAM, even if the app inside is tiny. A container carries almost nothing extra: the kernel is already running on the host, so starting a container is closer to starting an ordinary process than to booting a whole machine — milliseconds, and only the memory the app itself needs.
Two earlier ideas from this rung explain the lightness more precisely. First, a container's filesystem is usually an overlay filesystem: instead of giving each container its own full copy of the operating system files, the host keeps shared read-only base layers and stacks a thin writable layer on top for each container — so a hundred containers built from the same image cost roughly one copy of the image plus a hundred small diffs, not a hundred full copies. Second, the limits on a container's CPU and memory come from control groups (cgroups), which simply meter a normal process's resource use rather than reserving a fixed slab of a fake machine. Sharing, layering, and metering are exactly why containers feel so cheap.
Isolation: where the wall actually stands
Now the other side of the trade, and the reason VMs have not gone away. Isolation is about how tall and thick the wall between two workloads is — and crucially, what is on the shared side of that wall. With a VM, the wall is the hypervisor itself, and the only thing two guests share is the hypervisor's narrow, carefully guarded interface. Each guest has its own kernel, so a bug or compromise inside one guest's kernel stays trapped in that guest's fake machine; to escape, an attacker must defeat the hypervisor, a small and heavily scrutinised piece of code. That is why we say a VM offers strong isolation.
Containers draw the wall in a riskier place. The separation between containers is built from namespaces — each container sees only its own processes, its own network, its own filesystem mount tree, because the kernel hands it a filtered view — but every container is making real system calls into the very same host kernel underneath. The shared side of the wall is the entire kernel: a huge, complex surface. A flaw deep in that one shared kernel can, in principle, let a process in one container break out and touch another, or the host. This is weaker isolation: not weak in normal use, but weaker because what is shared is so much larger and so much more powerful than a hypervisor's thin interface.
So the rule to remember is exact: containers share the host kernel and therefore give lighter, weaker isolation; VMs run a full guest OS on a hypervisor and therefore give heavier, stronger isolation. Neither is 'better' — they answer different questions. Want to pack hundreds of cooperating microservices you wrote and trust onto one host, fast? Containers. Want to run code you do not trust, or two tenants who must never reach each other even if the kernel has a hole? A VM, or a container hardened with extra layers (a sandbox, a tiny per-container guest kernel) that deliberately buy back some of the VM's strength at some of its weight.
A worked comparison: starting forty copies
Make it concrete. Suppose you must run forty copies of the same small web service on one server with 32 GB of RAM. Walk the VM path first: each VM needs, say, a 512 MB guest OS just to exist, before the service's own 100 MB. Forty of them want about 40 times 612 MB, roughly 24 GB, much of it forty redundant copies of nearly the same kernel and services — and each takes tens of seconds to boot. The wall between them, though, is rock solid: forty separate kernels behind a hypervisor.
- Pull one shared image; its read-only layers are stored once on the host as an overlay base.
- For each of the forty containers, the kernel creates a fresh set of namespaces (own process list, network, mount tree) — a private view, not a private machine.
- Stack a thin writable layer per container on top of the shared base, so each can write without disturbing the others or the base.
- Apply a cgroup to each so it cannot starve the others of CPU or memory.
- Launch the service in each container as an ordinary process on the one shared kernel — all forty up in well under a second, sharing one kernel and one base image.
Now the container total: one copy of the shared layers plus forty times roughly 100 MB of actual service, perhaps 4-5 GB instead of 24 GB, started in under a second instead of minutes. The same forty workloads, a fifth of the memory, a hundredth of the start-up time. And the catch is exactly the one we named: those forty services now lean on one shared kernel, so they are isolated by namespaces and cgroups rather than by forty real walls. If all forty are your own trusted code, that trade is a bargain. If even one is hostile, you may want the heavier wall after all.
Living together: orchestration, migration, and the blur
Because a single host now holds dozens or hundreds of these light units, someone has to place them, restart the ones that crash, scale them up and down, and wire up their networking — by hand this is impossible at scale. That job is orchestration: a control system that treats a whole fleet of machines as one pool and schedules containers onto it, much as the OS scheduler you met in an earlier rung places processes onto CPUs, but one level up and across many machines. The orchestrator is to a cluster of hosts what the kernel is to one machine: the manager deciding who runs where.
VMs have their own fleet-level superpower, one containers find harder: live migration. Because a VM is a self-contained machine with its own kernel and a clean boundary at the hypervisor, the hypervisor can copy a running VM's memory to another physical host and switch over with barely a hiccup — moving a whole live operating system off a machine you need to take down for maintenance. Containers, being entangled with a specific host kernel's live state, are usually moved by stopping and restarting them elsewhere rather than migrating mid-flight; the very sharing that makes them light makes them harder to pick up and carry whole.
The whole rung in one balance
Step back and the entire rung resolves into one balance. The hypervisor and the techniques you learned for virtualizing the CPU, memory (nested page tables), and I/O all exist to make the heavy, strongly-isolated illusion — a full duplicate machine — efficient enough to be worth it. Namespaces, cgroups, and overlay filesystems all exist to make the light, weakly-isolated illusion — a private view of one shared machine — strong enough to be safe enough. Both are answering 'how do I let untrusting workloads coexist on one box?'; they just spend the budget differently between weight and the wall.
Hold onto the one sentence that survives every detail: a container shares the host kernel, which is why it is light and why its isolation is weaker; a VM runs a full guest OS on a hypervisor, which is why it is heavy and why its isolation is stronger. Every other fact in this rung — fast boot versus slow boot, one image versus forty copies, easy orchestration versus easy live migration — falls out of that single difference about what gets duplicated and what gets shared. Get that, and you can read any new tool in this space and immediately ask the only question that matters: where does it draw the wall, and what does it make you share?