Every Layer Here Is a Patch, Not a Preference

Every layer here is a patch, not a preference. You are about to pick one of these layers for something you build this year, bare metal, a VM, a container, a microVM, WASM, or a serverless function, and you will likely pick it without knowing why the other five exist. Nobody added any of them because they liked abstraction. Someone hit a wall, and the wall usually has a name and a date attached. Follow the dates back far enough and the story starts in a machine room in Cambridge, Massachusetts, with a line of engineers waiting their turn.

Bare metal

Picture that room. Through most of the 1960s, an IBM mainframe ran exactly one program at a time, for whoever had booked it, on a machine that cost more than most universities’ entire annual budget. Book your slot, run your job, and get out, because the next person is already waiting outside the door. That’s what bare metal actually looked like before the word existed: your code touching the CPU directly, zero overhead, full access to every cycle, and the entire machine held hostage by whoever got there first.

A team at IBM’s Cambridge Scientific Center, led by a researcher named Robert Creasy, decided the queue itself was the bug. Their fix shipped into production in January 1967. They called it CP-40, and what it did was close to sleight of hand: it gave every single user their own complete virtual System/360, memory, peripherals, all of it, running on top of one physical machine nobody had to line up for anymore. It’s the first hypervisor anyone built, and it exists for exactly one reason. Idle hardware is money on fire, and somebody finally got tired of watching it burn.

Virtual machines

CP-40 didn’t stay CP-40 for long. It became CP-67, then VM/370, and the family tree eventually produces VMware, KVM, and Hyper-V, all still running the same pitch six decades later: one physical machine, many guest operating systems, each one fully convinced it owns the hardware outright. Crash one and the others don’t even flinch, because the boundary between them is enforced in silicon, by extensions like Intel VT-x, not by software anyone has to trust.

The catch is that owning your own operating system means booting your own operating system, in full, every single time. A traditional VM takes thirty seconds to a minute just to open its eyes, dragging hundreds of megabytes of overhead behind it before your actual code runs a single line.

This breaks when you deploy fifty times a day. This breaks when traffic spikes and ten instances suddenly need to be ten thousand. This breaks when your CI pipeline wants a clean environment every few minutes and gets billed a full boot cycle just to say hello. Multiply thirty seconds by however many times a day that happens, and the wait stops being a footnote and starts being the whole workday.

Containers

So skip the second operating system. Docker and tools like it lean on Linux kernel features, namespaces and cgroups mostly, to give a process its own private view of the filesystem, the network, the process table, all without booting a separate kernel underneath it. Startup time falls to well under a second. Memory overhead falls to roughly ten megabytes. Where a rack used to hold ten VMs, it now holds a hundred containers.

The saving comes from the one thing containers give up to get it: the kernel. Every container on a host shares that host’s kernel, and on February 11, 2019, that stopped being an abstract worry and became a headline. Two researchers disclosed CVE-2019-5736, a flaw in runc, the low-level runtime sitting underneath both Docker and Kubernetes. A malicious container could overwrite the host’s own runc binary and walk out with root access on the machine itself, not just the container it started in. Red Hat, AWS, Google Cloud, and Docker all had advisories out inside the same week, five separate engineering teams patching the same wound at the same time.

Patch it and move on, you might say. That’s exactly what the industry did. But a patch closes one door. It doesn’t remove the wall the door was cut into, and the next escape just finds a different door in the same wall.

Micro-VMs

AWS had been living that exact tension for years before it had a name. Every Lambda invocation belongs to a different customer, and neither a VM’s thirty-second boot nor a container’s shared kernel was ever going to hold thousands of strangers’ code safely at the scale Lambda ran at. So a small team started writing a fix in Rust in the fall of 2017, open sourced it in 2018, and called it Firecracker. Take the isolation a full VM gives you and strip out everything that makes it slow: no BIOS emulation, no legacy device support, no general-purpose device model pretending to be a real computer. Just enough virtual machine monitor to boot a guest kernel and get out of the way.

The numbers are the whole pitch: under 125 milliseconds to boot, under 5 mebibytes of memory overhead per instance, up to 150 microVMs a second on a single host. Every Lambda invocation and every Fargate task since has run inside one of these. A dedicated kernel per workload, hardware isolation enforced by KVM, at a speed that used to belong only to containers.

It still boots a kernel, though. 125 milliseconds beats everything above it on this list. It loses to not booting a kernel at all.

WebAssembly

Fastly ran into the same wall from the other direction, out at the edge, where every hop back to a slower origin server is latency a visitor feels directly. So why boot a kernel at all? Don’t. Compile your code to WASM bytecode and hand it to a sandboxed runtime like Wasmtime or Wasmer, and you get memory-safe isolation with no hypervisor and no container runtime anywhere underneath it. Fastly built its own compiler and runtime for exactly this, called Lucet, and threw out the idea of booting anything at all. The number they published: 35.4 microseconds to start.

Put that next to Firecracker’s 125 milliseconds and you’re looking at roughly 3,500 times faster. Not a rounding difference between two similar approaches. A different category of thing entirely.

It won’t replace containers or micro-VMs anytime soon, though, whatever the benchmark suggests. WASI, the interface that gives WASM code access to files, sockets, and the rest of the outside world, is still being built out piece by piece. Languages with a garbage collector, Go and Python especially, produce bloated WASM binaries because they have to smuggle their own GC runtime into the sandbox with them. You’re swapping a decades-old, battle-tested syscall interface for a clean one that’s still under construction while you’re standing on it.

Serverless

Serverless isn’t another rung on this ladder. It sits on top of the whole thing and hides the ladder from you. Deploy a Lambda function and AWS decides whether it lands on a Firecracker microVM or something else entirely, and you’re not meant to know or care which.

Which sounds like it should have finally killed the boot time problem. It hasn’t. It just moved somewhere you can’t see it. The microVM itself boots in around 200 milliseconds, hidden behind a pool of pre-warmed instances you never interact with directly. The part you actually feel, downloading your function’s code and running its initialization, can stretch to sixty seconds for a bloated deployment package, or shrink to twenty milliseconds for a small static binary. Serverless never got rid of the tradeoff between boot time and package size. It buried it in your dependency list, where you won’t find it until a customer does, in production, at the worst possible moment.

None of this stays pure

Walk into any real production environment and the clean single layer disappears. Kubernetes clusters get run inside VMs, blending VM-grade tenant isolation with container-level agility. Plenty of teams skip the hypervisor entirely and run containers straight on bare metal, because there’s only one tenant and nothing to isolate from. Kata Containers wraps an ordinary container image inside a Firecracker-style microVM, so a team keeps the Docker workflow it already knows while handing untrusted, multi-tenant code real hardware isolation underneath. The chain you just walked, top to bottom, doubles as a shopping list. Most real systems buy from three or four rows of it at once.

Every layer here is a patch, not a preference.

Takeaways

  1. Bare metal wastes the most expensive resource in the room, idle hardware. Virtualization, invented in 1967, traded raw speed for the ability to share it.
  2. Full VMs are safe but slow to boot, thirty to sixty seconds. Containers gave up some of that isolation for a boot time under a second.
  3. Containers share a kernel. CVE-2019-5736 proved that one bug in that shared boundary can hand an attacker root on the host, not just the container.
  4. Micro-VMs like Firecracker rebuild hardware isolation at container speed: under 125 milliseconds to boot, under 5 mebibytes of overhead.
  5. WebAssembly skips the kernel boot entirely, 35.4 microseconds to start, but WASI still hasn’t caught up to what a real OS gives you for free.
  6. Serverless doesn’t remove any of these tradeoffs. It picks one for you and hides the seam, until a cold start puts the seam right back in front of you.

Published September 26, 2026