Virtual machines, containers, serverless. Three ways to run your code, endless articles comparing them, and yet a lot of people still cannot say clearly why you would pick one over another. The confusion comes from comparing them on the wrong axis. Here is the mental model that made it click for me: each one is a different answer to "how much of the machine do you carry with your app," and once you see them that way, when to use which becomes obvious.
The single axis: how much you carry
Think of running your code as packing for a trip. The question is how much of the environment you bring along.
- Virtual machine: you bring the whole house. Your app, plus a full operating system, plus everything.
- Container: you bring a suitcase. Your app plus just its dependencies, sharing the host's operating system.
- Serverless: you bring yourself. Just your code, and the platform supplies everything else on demand.
Everything else, cost, speed, control, follows from how much you carry.
Virtual machines: the whole house
A VM virtualizes hardware. Each VM runs its own full operating system on top of a hypervisor that slices up a physical machine. Your app runs inside that complete OS.
- You get: strong isolation (each VM is a separate OS), full control of the environment, the ability to run anything.
- You pay in: weight. Each VM carries a whole OS, so they are large, slow to start (minutes), and you are responsible for patching and maintaining every one of those operating systems.
Use a VM when you need full control of the OS, strong isolation, or you are running something that expects a complete machine.
Containers: the suitcase
A container packages your app with its dependencies but shares the host operating system's kernel instead of carrying its own. That is the whole difference from a VM, and it changes everything:
- You get: lightweight, fast-starting (seconds), portable units that run the same on your laptop and in production. You can pack many containers onto one host because they are not each dragging a full OS.
- You pay in: slightly weaker isolation than VMs (they share a kernel), and you need something to orchestrate them at scale (Kubernetes, ECS).
Use containers when you want consistent, portable deploys and efficient use of your machines, which is most modern services. Containers are the default unit for a reason: they are the sweet spot between control and efficiency.
Serverless: just bring yourself
Serverless (like AWS Lambda) means you provide only your code, and the platform runs it on demand, provisioning and tearing down the compute invisibly. There is a server, you just never see or manage it.
- You get: zero infrastructure to manage, automatic scaling (including to zero), and you pay only while your code actually runs. Nothing to patch, nothing to size.
- You pay in: less control, cold starts (the first request after idle waits for a spin-up), execution limits, and a poor fit for long-running or steady heavy workloads.
Use serverless when your work is event-driven, bursty, or intermittent, and you want to manage nothing. Perfect for glue code, webhooks, and jobs that fire occasionally.
They are not mutually exclusive
The real world mixes all three. Serverless functions often run in containers under the hood. Containers run on VMs. A single system might use VMs for a legacy database, containers for its main services, and serverless for occasional event handlers. The question is never "which one for my whole company," it is "which one for this workload."
The decision, in three questions
- Is the work event-driven, short, and intermittent, and do you want to manage nothing? Serverless.
- Is it a service you want portable, efficient, and consistent across environments? Containers.
- Do you need full OS control, strong isolation, or to run something that expects a whole machine? VM.
And the cost shape, roughly: serverless wins for spiky low-volume work (pay per run), containers win for steady services (efficient packing, no per-OS overhead), VMs make sense when you need the control or isolation and can keep them well-utilized.
The take
Stop comparing containers, VMs, and serverless on a feature checklist and compare them on one axis: how much of the machine you carry with your app. A VM brings the whole OS (heavy, isolated, full control). A container brings just dependencies and shares the kernel (light, portable, the modern default). Serverless brings only your code (zero management, scales to zero, less control). Match the amount you carry to the shape of the workload, mix them freely, and the choice stops being confusing.
Which one did this finally click with for you? For me it was understanding that a container shares the host kernel while a VM carries its own OS, that one sentence explained every difference in speed, size, and isolation at once.
Top comments (0)