We ended
This final lesson is about the engineers who refused the dilemma and built new points on the spectrum. Three ideas close the isolation gap without paying the full VM tax: gVisor (a user-space guest kernel that intercepts syscalls), microVMs like Firecracker (a hypervisor stripped to the studs, booting a real kernel in ~125 ms), and unikernels (compile the app and only the OS libraries it needs into one tiny image with no syscalls at all). Each trades a little overhead for a lot of isolation, and together they turn the binary "container or VM?" into a rich spectrum you can dial along.
The right way to hold all of this is a single line, ordered by how strong the isolation boundary is — and, because it is the same axis, by how much overhead each option carries. From a bare process (no isolation, zero overhead) to a full VM (hardware boundary, seconds and hundreds of MB), each rung buys isolation with overhead.
| Approach | Isolation boundary | Boot | Overhead | Where it's used |
|---|---|---|---|---|
| Process | none (same OS view) | <1 ms | zero | ordinary programs |
| Container | namespaces on a shared kernel | ~ms | near zero | microservices, packaging |
| gVisor | user-space guest kernel intercepts syscalls | tens of ms | syscall-heavy: noticeable | GKE Sandbox, App Engine |
| microVM | real guest kernel, minimal hypervisor | ~125 ms | a few % | AWS Lambda, Fargate |
| Unikernel | single-purpose image on a hypervisor | ~ms | tiny (no syscalls) | appliances, edge, research |
| Full VM | hardware-enforced, full guest OS | seconds | a few %, big memory | general virtualization, IaaS |
Notice that gVisor, microVMs, and unikernels all land between the container and the full VM — that middle band, "container-fast with VM-ish isolation", is exactly the gap they were invented to fill.
Google's gVisor takes a startling approach: instead of letting the container's syscalls
hit the host kernel, it intercepts them and services them in a sandbox process called
The payoff is a dramatically smaller host-kernel attack surface: the container can no longer reach hundreds of host syscalls directly, only the handful gVisor forwards. The cost is performance — every intercepted syscall is now a user-space round trip through the Sentry, so syscall-heavy workloads (lots of tiny I/O) slow down noticeably, while compute-bound workloads barely notice. It is the microkernel trade-off from earlier in the course, reincarnated for container isolation: put a software layer between the workload and the kernel, pay in indirection, gain in safety.
Firecracker asked: why is a VM slow to boot? Not because hardware virtualization is slow — that part is fast — but because general-purpose VMMs (QEMU) emulate a whole PC: BIOS, PCI, dozens of legacy devices. Firecracker throws almost all of it away, keeping a minimal device model (a few virtio devices, a serial port) and a stripped boot path. The result is a real guest kernel with a hardware-enforced boundary that boots in about 125 ms and adds only a few MB of overhead — the isolation of a VM at nearly the agility of a container. It runs under AWS Lambda and Fargate, launching millions of microVMs to isolate every tenant's function.
A unikernel attacks from the opposite side. Rather than shrink the VM around a general OS,
it shrinks the OS to nothing but what one app needs: you compile the application together with
just the library-OS pieces it calls (a TCP stack, a scheduler, a filesystem — as libraries) into a single
image that runs directly on the hypervisor in one address space. There is no user/kernel split
and no syscalls — a "syscall" is just a function call. The image is tiny (megabytes), boots in
milliseconds, and has a minuscule attack surface because code the app never uses simply isn't there.
Three different shapes, one shared goal: shrink what the untrusted code can touch. gVisor shrinks the reachable host kernel; Firecracker shrinks the VM around a real kernel; the unikernel shrinks the OS itself down to one app.
Let's put rough numbers on the spectrum and let the ordering emerge. The simulation models each approach by two coordinates — a boot time and a relative "attack-surface" score (how much host-kernel code the untrusted workload can reach) — and sorts them, so you can see the trade curve rather than memorise a table.
For years the answer was awkward: Lambda packed functions into containers, but multi-tenant containers on
a shared kernel are a security worry, so AWS actually ran one full VM per customer and packed
that customer's functions inside — safe, but wasteful and slow to scale. Firecracker dissolved the
compromise. Now each function instance gets its own microVM: a real, hardware-isolated guest
kernel that boots in about
Two traps. First, do not read this lesson as "microVMs beat containers, always use them." Every rung up
the isolation ladder costs something — gVisor taxes syscall-heavy workloads, microVMs add ~125 ms and a
few MB per instance, unikernels demand you rebuild your app against a library OS and lose the rich
debugging and tooling of a normal Linux. If your tenants already trust each other, a plain container is
the right, cheapest tool. Second, a unikernel is not a lightweight container: it has no shared
host kernel, no