Containers vs VMs vs serverless
Published
Three ways to rent a place for your code to run, from most of the building to a seat by the hour.
- A virtual machine is a flat of your own inside a building. Own kitchen, own lock, own meter. A hypervisor splits one physical server into several, each running its own full operating system.
- A container is a room in a shared flat. Your own door and your own stuff, but everyone shares the one kitchen, which here is the host's operating system kernel. Much lighter, and the walls are thinner.
- Serverless (functions as a service, like AWS Lambda) is a co-working desk by the minute. You bring only your code; the provider finds a desk when a request arrives, and you pay only while you sit there.
A coupon validation endpoint gets 5 requests a second all day, and 4,000 a second for ten minutes when a sale starts at 8 pm. Would you run it on a VM, in containers on Kubernetes, or as Lambda functions? What could go wrong at 8:00:01 pm?
The details that decide#
Isolation. VMs are isolated by hardware virtualisation; a container escape is a kernel bug away. That is why clouds run each customer's containers inside VMs, and why AWS built Firecracker microVMs (open sourced in 2018) to give Lambda and Fargate VM isolation with start times around a hundred milliseconds.
Start time. VMs: tens of seconds. Containers: under a second plus your app's startup. Serverless: warm invocations are instant; a cold start (no warm sandbox free) adds from around a hundred milliseconds for small Node or Python functions to seconds for large JVM ones.
Limits. Lambda caps a run at 15 minutes, has a per account concurrency limit (1,000 by default per region, raisable), and is stateless between invocations. Containers and VMs can run forever and keep warm caches and connection pools.
Databases. A thousand concurrent functions can mean a thousand database connections. Serverless apps put a proxy in front (RDS Proxy, PgBouncer) or use databases built for it (DynamoDB, serverless Postgres with HTTP drivers).
Cost: it depends on how busy you are#
Serverless bills per request and per GB-second; a container or VM bills per hour whether or not anyone calls it. So serverless wins at low or spiky traffic and loses at high steady traffic.
Approximate x86 list prices for us-east-1 and a rough per container throughput; your numbers will differ. The shape does not: per request pricing wins when traffic is low or idle much of the day, and always on capacity wins once it is busy around the clock.
| Choice | What you gain | What you pay | Pick it when |
|---|---|---|---|
| Virtual machines | Strong isolation, any OS or kernel, full control, long running and stateful work. | Slow to start, you patch and manage the OS, pay while idle. | Databases you run yourself, legacy apps, special kernels or hardware, Kubernetes nodes. |
| Containers (Kubernetes, ECS, Cloud Run) | Portable images, fast start, dense packing, warm pools and caches, predictable cost at scale. | An orchestrator to run (or pay for), weaker isolation than VMs, pay while idle. | Most services with steady traffic, microservices, anything long running. |
| Serverless functions | Zero servers to manage, scale to zero, near instant scale out, pay per use. | Cold starts, time limits, connection storms, vendor lock in, expensive under steady load. | Spiky or low traffic APIs, event handlers (uploads, queues, webhooks), cron jobs, glue. |
At the moment a sale starts, thousands of new function instances start cold at once, each paying its start up time and each opening a database connection. Users see slow first requests and the database sees a connection flood. Provisioned concurrency (pre warmed instances), a connection proxy, and keeping functions small fix most of it. Or run the hot path in containers and keep functions for the edges.
- DockerBuilds and runs container images
- KubernetesOrchestrates containers across many VMs
- AWS LambdaFunctions in Firecracker microVMs
- Cloudflare WorkersV8 isolates at the edge, millisecond starts
- Google CloudCloud Run: containers that scale to zero
- VercelServerless and edge functions for web apps
In the interview#
Checkpoint#
1. What is the core difference between a container and a VM?
2. When does serverless become more expensive than containers?
3. What is a cold start?
- VM: own kernel on a hypervisor. Container: isolated process, shared kernel. Function: just code.
- Start time: minutes, sub second, milliseconds warm (or a cold start).
- Serverless wins spiky and low traffic. Containers win steady and high.
- Lambda: 15 minute limit, concurrency limits, watch DB connections.
- Firecracker microVMs give functions VM grade isolation.
A virtual machine runs a full operating system on a hypervisor: strong isolation, any OS, long running and stateful work, but slow to start and you manage the OS and pay while idle. A container is a process isolated by kernel namespaces and cgroups that shares the host kernel: it starts in under a second, images are small and portable, and orchestrators like Kubernetes pack many onto VMs, at the cost of weaker isolation. Serverless functions hide the server: the platform runs a copy per concurrent request, scales from zero, and bills per request and millisecond, with cold starts, time limits, concurrency limits and database connection storms as the catches; Lambda uses Firecracker microVMs for isolation. I run steady services in containers, use functions for spiky or event driven edges and early low traffic products, and use VMs for stateful infrastructure or special OS needs.