Compute

Choosing a Compute Model

Virtual machine, container, managed container platform, or functions. Six questions decide it, and every answer must come with why, what it costs, and what you would use instead.

▶ Run the lab

The question this answers

Infrastructure question

Which execution model fits this workload, and what am I giving up by choosing it?

Application requirement

A team must place a workload — knowing its shape, its traffic pattern, its state, its isolation requirements and, crucially, how many people are available to operate whatever they choose.

What it provides

A defensible placement with its costs stated: startup characteristics, operational surface, isolation strength, scaling behaviour and cost shape, each traceable to a property of the workload.

Application RequirementInfrastructure RequirementComputeNetworkStorageIdentityDeploymentScalingReliabilityObservabilitySecurityCostTrade-offs

Four models, compared on what actually differs

The four models are not a maturity ladder and functions are not the "modern" answer. They are four different points on a trade between control and operational surface, and the right one depends far more on the workload and the team than on the technology.

A virtual machine gives you a whole operating system: any runtime, any daemon, custom kernel modules, long-lived processes, and a strong isolation boundary. You own patching, image building and configuration management, and you scale in units of minutes. A container on your own hosts gives you packaging and density with a much weaker isolation boundary and the same host-level obligations. A managed container platform — from a simple task runner up to Kubernetes — takes placement, restarts and rollouts off your hands, and hands you a control plane to understand and upgrade in return. A function platform takes everything below the code, and hands you cold starts, execution limits, enforced statelessness and a concurrency model you do not control.

The single most under-weighted variable in this decision is the *operator*, not the workload. A managed container platform with a two-person team and no prior experience is a different decision from the same platform with a platform team of eight — the software is identical and the expected outcome is not. This is No Cargo-Cult Infrastructure applied to the most consequential choice in the domain.

DimensionVirtual machineContainer (self-hosted)Managed container platformFunctions
Time to first requestTens of seconds to minutesSecondsSecondsMilliseconds warm; hundreds of ms to seconds cold
You own fromGuest OS upwardHost OS + image upwardImage upwardCode upward
Isolation strengthStrong — separate kernelWeaker — shared kernelWeaker — shared kernel, per-tenant nodesProvider-managed, usually per-tenant sandboxing
Long-running processesYes, indefinitelyYesYesNo — hard execution ceiling per invocation
Custom OS, kernel modules, daemonsYesLimited — shares the host kernelRarelyNo
Scaling unit and speedInstance, minutesContainer, seconds (capacity permitting)Container, seconds; nodes in minutesInvocation, instant to the caller
Idle costFull instance rateFull host rateNode rate + control planeNear zero
Cost shape at high steady loadPredictable and usually cheapest per unitCheapest per unit at high densityGood, plus control-plane overheadOften the most expensive — per-invocation adds up
Operational surfaceOS patching, config management, imagesAll of that plus a runtimeControl plane, upgrades, scheduling, networkingAlmost none — and almost no visibility
The dimensions that actually differ. Read across your workload's constraints.

Six questions, in order

The questions below are ordered so that the ones that *eliminate* options come first. A hard execution ceiling rules out functions for a two-hour job regardless of every other consideration, so ask it before asking about cost.

Question one: does the work outlive a request? A batch job, a stream consumer, a WebSocket server or a background poller needs a long-lived process, which removes functions. Question two: does it need control of the operating system — a custom kernel module, a specific kernel version, a licensed daemon, deterministic scheduling? That pushes toward a virtual machine or bare metal. Question three: what does the traffic look like? Genuinely spiky, low-average, event-driven traffic is the case functions were built for; steady traffic is the case they are worst at.

Question four: how strong must the isolation boundary be? Running untrusted or tenant-supplied code across a shared kernel is a materially weaker boundary than a hypervisor — see Containers vs Virtual Machines. Question five: what operational capacity does the team actually have? and question six: what is the cost shape at expected steady load? Notice that cost is last. It is the question people ask first and the one most likely to be wrong, because the cost curves cross: functions are far cheaper at low volume and frequently far more expensive at high steady volume — see Serverless Trade-offs.

  • Long-running? Removes functions. Stream consumers, WebSocket servers and multi-hour jobs need a process, not an invocation.
  • OS control? Custom kernel, licensed daemon or deterministic latency pushes to a VM or bare metal.
  • Spiky and low-average? The strongest case for functions: near-zero idle cost and instant elasticity.
  • Untrusted code? A shared kernel is a weaker boundary. Tenant-supplied code wants VM-grade or microVM-grade isolation.
  • Operational capacity? The question that most often should change the answer and most often does not get asked.
  • Cost shape? Last, because the curves cross — cheap at low volume is frequently expensive at steady high volume.
Elimination first, preference last
noyesyesno / steadyyesnohard isolationordinaryno — and that is fineyes, and something forces itWork outlives a request?Traffic spiky and low-average?Needs OS control, kernel, daemons?FunctionsUntrusted code / hard isolation?Team can operate a control plane?Virtual machineManaged container serviceOrchestrated cluster
ClientGateway / LBServiceWorkerDatabaseCacheQueue / LogObject storageCDNExternal system

The same workload, placed twice

Consider a queue-driven media-processing worker: each job takes three to eight minutes of CPU-heavy work, jobs arrive in bursts, losing a job mid-flight is acceptable because it will be redelivered, and there are four engineers.

Placing it on functions fails at question one: the execution ceiling is below the job duration for the long tail. Placing it on an orchestrated cluster fails at question five: four engineers, no prior operational experience, and nothing in this workload requires a scheduler. The workload has one shape — pull, process, acknowledge — with no service discovery, no rolling-update complexity and no inter-service networking.

A managed container service on interruptible capacity, scaled on queue depth, fits every property: containers start in seconds, the work is naturally idempotent and resumable, interruptible capacity is dramatically cheaper for exactly this shape, and the operational surface is a task definition and a scaling rule. The comparison below is not "less YAML is nicer". It is that the second design has no failure modes the workload did not already have.

Orchestrated cluster: four engineers, one queue worker
cluster:            managed kubernetes, 2 node groups, 2 environments
installed:
  - ingress controller        # nothing here serves HTTP
  - cert-manager              # no certificates needed
  - metrics server + KEDA     # to scale on queue depth
  - cluster autoscaler        # to add nodes for the above
workload:
  Deployment + HPA + ServiceAccount + NetworkPolicy
  + PodDisruptionBudget + node selectors for CPU-heavy pods

new failure modes acquired:
  unschedulable pods, node pressure evictions,
  control-plane version upgrades, CNI issues,
  scaling that depends on three components agreeing
operators: 4, none of whom has run a cluster before
Managed container service: same workload, same throughput
service:
  image:            worker:sha-9f3c1a2      # pinned by digest
  cpu: 2, memory: 4Gi
  capacity:         interruptible           # jobs are redelivered
  scale_on:         queue_depth
    min: 0
    max: 40
    target_per_task: 5 messages
  stop_timeout:     600s                    # longest job + margin

what you own: the image, the scaling rule, the queue
what you do not own: nodes, scheduler, control plane, upgrades
failure modes: the ones the workload already had
  (poison message, slow job, capacity reclaimed mid-job
   -> all handled by redelivery, which you needed anyway)

Both designs process the same queue at the same rate. The first one adds a scheduler to a workload with nothing to schedule, an ingress controller to a workload that serves no HTTP, and four new outage classes to a team of four. If a second workload arrives with genuine placement, discovery and rollout complexity, the cluster becomes a defensible answer — that is the forcing event, and it has not happened yet. See Kubernetes Is Not Always Needed.

Key points

  • Four models trade control against operational surface; none is more modern or more correct than the others in the abstract.
  • Ask the eliminating questions first: long-running work removes functions, OS control removes everything but a VM, untrusted code demands a stronger boundary than a shared kernel.
  • Team operational capacity belongs in the decision, and it is the variable most often omitted and most often decisive.
  • Cost is the last question because the curves cross: functions are cheapest at low, spiky volume and frequently the most expensive at steady high volume.
  • A managed container service sits between a VM and an orchestrator and is the right answer far more often than either — it is also the one nobody proposes in a design review.

The loop, answered

Every field is required, which is why no lesson here can recommend something without saying what it costs and what simpler thing to consider first.

How it works
  • A virtual machine gives a guest kernel a share of a physical host, so you get a full operating system with a strong boundary and a slow start.
  • A container shares the host kernel and isolates with namespaces and cgroups, giving fast starts and high density with a weaker boundary.
  • A managed container platform adds a control loop that places containers on hosts, restarts them, replaces them on rollout and registers them for traffic.
  • A function platform holds no instance for you between invocations: it creates an execution environment on demand, reuses it while traffic continues, and reclaims it when idle — which is exactly what a cold start is.
What you still own
  • Virtual machine: OS patching, image building, configuration management, and instance replacement — see The VM Lifecycle.
  • Self-hosted containers: everything above plus a runtime, plus whatever supervises the containers.
  • Managed container platform: the image, the task or workload definition, the scaling rule, and — with an orchestrator — the control plane and its upgrade cadence.
  • Functions: the code, its dependencies, its permissions, its memory setting, and the connection problem it creates against a pooled database — see Serverless and Database Connections.
  • In every model: the identity, the network exposure, the observability and the bill.
How it fails
  • Functions chosen for a job that occasionally exceeds the execution ceiling: it works for months and fails on the largest input, which is the one that mattered.
  • Functions in front of a pooled database: a burst creates hundreds of concurrent execution environments, each opening connections, and the database refuses them all.
  • An orchestrated cluster adopted without a forcing event: outages caused by unschedulable pods, node pressure and control-plane upgrades — failures the workload did not previously have.
  • A virtual machine fleet with drifted, hand-patched hosts, where a replacement no longer matches production because the changes were never in an image — see Mutable Servers and Immutable Images.
  • A container fleet with a large image and a slow start, so autoscaling arrives after the burst it was meant to absorb.
How it scales
  • Functions scale per invocation and hit account concurrency limits and downstream connection limits, not compute limits.
  • Containers scale in seconds up to the capacity of the underlying hosts, and then in minutes as nodes are added — a two-tier lag that surprises people.
  • Virtual machines scale in minutes, which means bursts must be absorbed by warm headroom rather than by reaction.
  • In every model the real ceiling is usually downstream: database connections, a third-party rate limit, or a provider quota.
Security
  • Isolation strength differs materially: a hypervisor boundary is stronger than a shared-kernel container boundary, which matters most for untrusted or tenant-supplied code.
  • Each model has a different patching obligation, and the ones where you own the guest OS are the ones that quietly go unpatched.
  • Functions have the smallest attack surface you operate and the least visibility into what happened, which is a genuine trade rather than a pure win.
  • Identity is uniform across models and is where most real incidents happen regardless of which you chose — see Least Privilege in Infrastructure.
Cost shape
  • Virtual machines and containers bill for existence; functions bill for invocation and duration, with near-zero idle cost.
  • The curves cross. Below some steady request rate functions are cheaper; above it, provisioned capacity is, often by a large factor.
  • An orchestrated cluster adds a control-plane charge and a node-utilization problem: you pay for whole nodes and schedule fractions onto them.
  • Interruptible capacity is available to VM and container models and not to functions, and it is the largest single discount in the compute space.
What to watch
  • Time-to-first-request per model — cold start for functions, time-to-ready for containers and VMs. It decides what your scaling can actually respond to.
  • Concurrency and throttling for functions; node utilization and pending-placement counts for orchestrated clusters; per-instance saturation for VMs.
  • Cost per unit of business work, which is the only way to compare models honestly across very different billing shapes.
  • The signal that lies: a function platform's per-invocation duration metric. It excludes cold-start time in some implementations, so the platform looks fast while users wait.
Simpler alternatives
  • A managed application platform (PaaS) above all four, if the workload is an ordinary web service. It removes the entire decision and is right for a surprising number of teams for a surprising number of years.
  • A single virtual machine running the process under a supervisor, for a small steady internal service — no orchestrator, no images to build, no control plane.
  • A managed container service rather than an orchestrator, whenever the answer to "what forced the scheduler?" is unconvincing. This is the most commonly correct and least commonly proposed answer.
  • Mixing models deliberately: a container service for the API and functions for genuinely event-driven glue is coherent, provided the number of distinct models stays small enough to operate.
What adopting this costs
  • Every step up the abstraction removes operational surface and removes diagnostic reach into exactly the layers you gave away.
  • Functions buy elasticity and near-zero idle cost and charge cold starts, execution ceilings, enforced statelessness and a connection problem.
  • Orchestration buys placement, self-healing and rollout automation and charges a control plane, a new failure vocabulary and a real learning curve.
  • Virtual machines buy control and strong isolation and charge patching, slow scaling and image management.

Which compute model?

Which compute model?
Six questions about the workload — never about the technology. The reasoning is shown in full, because the verdict is worth much less than the argument that produced it.
1 · traffic shape
2 · custom OS, kernel module, driver or GPU?
3 · how long does one unit of work run?
4 · isolation
5 · operational capacity
6 · must it run elsewhere later?
Virtual machine
score +1
Self-managed containers / orchestrator
score -4
Managed container platform
score +8
Serverless functions
score -4
Reasoning
A long-running listener wants a running process; per-invocation billing has no advantage when the invocation never ends.
Work that runs for minutes hits execution ceilings — that is a hard wall, not a tuning knob.
One or two engineers cannot also be a platform team; every self-managed control plane is a second product to run.
Why
You want the container as the deploy unit — reproducible image, any language, no OS to patch — without owning a scheduler. This is the default answer for most web services in most companies.
Trade-offs
Less control over scheduling, networking and node-level tuning, a per-request or per-vCPU-second bill, and platform limits (request timeout, image size, concurrency) you have to design inside rather than around.
Alternatives
If it is genuinely one small always-on service, a VM with a process supervisor is simpler and cheaper. If it is genuinely event-driven and short, functions remove even the always-on floor.
Managed container platform. The margin over Virtual machine comes from a hard constraint rather than a preference — a long-running listener wants a running process; per-invocation billing has no advantage when the invocation never ends. Constraints like that are worth respecting; the rest of the answers only shift the runner-up. Re-run this the moment the workload changes shape, because the answer is a property of the workload, not of the company.
ILLUSTRATIVEthe weights are a teaching device; a real decision also weighs the team, the deadline and what already runs

What people believe, and what is true

Claim

Serverless is cheaper.

Reality

It is cheaper for spiky, low-average workloads because idle costs almost nothing. For steady high-volume traffic it is frequently several times the cost of provisioned capacity — see Serverless Trade-offs.

Claim

Containers are always better than VMs.

Reality

They are lighter and start faster with a weaker isolation boundary. For untrusted code, custom kernels, licensed daemons or deterministic latency, a VM is the correct answer.

Claim

Kubernetes is the destination once you outgrow simpler things.

Reality

It is the answer to a specific set of problems — many workloads on shared machines, complex placement, per-service rollouts across teams. Most systems never acquire those problems — see Kubernetes Is Not Always Needed.

Go deeper

Overview

Four models: VM, container, managed container platform, functions. Ask what the workload needs — long-running, OS control, isolation, traffic shape — and what the team can operate.

Practical

Run the six questions in order. Eliminate first, prefer last. Write the answer down with the reason, and record what would change it — a workload that becomes steady, or a second workload with real placement complexity.

Advanced

Model the cost curves for your actual request rate and duration before deciding on price; the crossover between per-invocation and provisioned billing is the whole argument and it moves with memory setting and commitment discounts. Then check the downstream limits, because concurrency almost always hits a database or a third-party rate limit before it hits a compute limit.

Internals

The models differ in what boundary the provider uses and when it is created. A VM boundary is a hypervisor and is created once, slowly. A container boundary is namespaces and cgroups on a shared kernel, created in milliseconds. A function platform creates a sandbox per concurrent invocation and reclaims it when idle — so a cold start is boundary creation plus runtime initialization plus your own startup code, which is why your dependency graph is part of your latency budget.

Apply it