Quick Recap
In Lesson 1 we established: containers are standardized, isolated packages that bundle an application with everything it needs to run. They're portable, lightweight, and reproducible.
But if you've worked with Virtual Machines before, this might sound familiar. So what's actually different? Let's peel back the layers.
Virtual Machines Explained
A Virtual Machine emulates an entire computer. A piece of software called a hypervisor sits between the physical hardware and one or more guest operating systems, giving each VM the illusion of having its own dedicated hardware.
The VM Stack
- Hardware — physical CPU, RAM, disk, NIC
- Hypervisor — (Type 1: bare-metal like ESXi, Xen; Type 2: hosted like VirtualBox)
- Guest OS — full kernel + userland (e.g., a complete Ubuntu install)
- Application — your code, finally running on top
Each VM carries its own kernel, system libraries, and init system. That's a lot of overhead just to run one app — but it provides very strong isolation.
Containers Explained
Containers take a fundamentally different approach: instead of virtualizing hardware, they share the host's kernel and isolate at the process level using two Linux primitives:
- Namespaces — give each container its own view of the system (PID, network, mount, user, etc.)
- cgroups — limit and account for resource usage (CPU, memory, I/O)
The Container Stack
- Hardware — same physical machine
- Host OS + Kernel — one shared kernel
- Container Runtime — (Docker, containerd, CRI-O)
- Application — isolated process(es) with their own filesystem view
No guest OS boot. No duplicate kernel. Just process isolation with a thin abstraction layer.
Side-by-Side Comparison
VMs are like separate houses. Each has its own plumbing, electricity, foundation, and roof. Fully independent, but expensive to build and maintain.
Containers are like apartments in a building. They share the foundation, plumbing, and electrical systems (the kernel), but each unit has its own locked door, furniture, and layout (isolated filesystem and processes).
Key Differences at a Glance
| Attribute | Virtual Machines | Containers |
|---|---|---|
| Startup Time | Minutes | Seconds (or less) |
| Image Size | GBs (full OS) | MBs (app + deps only) |
| Isolation Level | Hardware-level (very strong) | Process-level (kernel shared) |
| Resource Overhead | High (each VM runs a kernel) | Low (shared kernel) |
| Density | ~10s per host | ~100s–1000s per host |
| OS Support | Any OS (Linux, Windows, BSD) | Must match host kernel type |
| Portability | Portable but heavy | Extremely portable & lightweight |
When to Use Which
Choose VMs when you need:
- Strong multi-tenant isolation — untrusted workloads that must not share a kernel
- Different OS kernels — running Windows alongside Linux on the same hardware
- Legacy applications — software that expects a full OS environment or specific kernel versions
- Compliance requirements — regulatory rules mandating hardware-level separation
Choose containers when you need:
- Microservices — many small, independently deployable services
- CI/CD pipelines — fast, reproducible build and test environments
- Rapid scaling — spinning up instances in seconds, not minutes
- Development environments — "works on my machine" finally solved
- High density — running many workloads on limited infrastructure
The Hybrid: VMs Running Containers
In production, it's rarely "either/or." The most common pattern is containers running inside VMs:
- VMs provide the infrastructure isolation boundary (between teams, tenants, or security zones)
- Containers provide application packaging and orchestration within those VMs
This gives you the best of both worlds: strong outer isolation + lightweight inner deployments.
What if you want container ergonomics with VM-level isolation? That's exactly what these technologies provide:
- Kata Containers — each container runs inside its own lightweight VM. Compatible with the OCI spec, so you use standard container tooling but get hardware isolation.
- Firecracker — Amazon's microVM technology. Boots a minimal VM in ~125ms with as little as 5MB of memory. Powers AWS Lambda and Fargate.
- gVisor — Google's approach: a user-space kernel that intercepts syscalls, providing sandboxing without a full VM.
These blur the line between containers and VMs, giving operators a spectrum of isolation options.
Industry Context
- AWS Fargate — runs your containers, but each task gets its own Firecracker microVM under the hood. You get container simplicity with VM isolation.
- AWS Lambda — functions run in Firecracker microVMs. Sub-second cold starts, strong isolation between tenants.
- Google Cloud Run — containers on gVisor-sandboxed infrastructure.
- Azure Container Instances — Hyper-V isolated containers for multi-tenant security.
The trend is clear: the industry is converging on containers for the developer experience, with VM-like isolation happening transparently underneath.
🎯 Key Takeaways
- VMs virtualize hardware — each runs a full OS kernel. Strong isolation, heavy overhead.
- Containers virtualize the OS — they share the host kernel and isolate via namespaces/cgroups. Fast, lightweight.
- Startup: containers in seconds; VMs in minutes.
- Density: hundreds of containers per host vs. tens of VMs.
- It's not either/or — production systems commonly run containers inside VMs.
- The industry is converging on microVMs (Firecracker, Kata) that blend the strengths of both.