Library
9 min read

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.
Your answer

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?

What each layer shares
Each VM has a complete operating system, so it is isolated by hardware virtualisation and can run anything. Boots in tens of seconds to minutes, carries gigabytes of OS, and you patch that OS yourself. An EC2 instance is a VM.

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.

Monthly cost, Lambda vs always on containers
50
100 ms
512 MB
Requests per month130 million
Lambda (requests + GB-seconds)$134
Containers: 2 x about $60 (min 2 for failover)$120
Containers are cheaper
$120 vs $134 a month

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.

ChoiceWhat you gainWhat you payPick it when
Virtual machinesStrong 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 functionsZero 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.
The 8 pm cold start stampede

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.

Names you will hear
  • 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#

Interviewer
Why not run the whole backend on Lambda?
They want the conditions where serverless stops being a good deal.
You
For the main API with steady traffic all day, containers are cheaper and give me warm connection pools and caches, no cold starts, and no 15 minute limit. I would use Lambda for the spiky and event driven edges: image processing on upload, webhook receivers, nightly jobs. If the main API had very low or unpredictable traffic, early on for example, Lambda would be the cheaper and simpler start, with a connection proxy in front of the database.
Splits by traffic shape, and names the database connection issue.
VM, container or function?
Generate a thumbnail every time a photo is uploaded to S3
A self managed Kafka cluster
Thirty microservices with steady daytime traffic
An app that needs a custom kernel module

Checkpoint#

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?

Worth memorising
  • 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.
Say this in 60 seconds

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.