genaipros← The path
Line D · CloudC3 · Compute & GPUs

Cloud for GenAI · Line D · C3 of the curriculum

Compute — how much of the machine do you still want to touch?

One dial, three dialects. This stage teaches the abstraction ladder vendor-neutrally, then shows how AWS, Azure, and Google Cloud each implement every rung — and how to defend the cost trade-off out loud.

5 tabs · all written Concepts before consoles Sources verified July 2026 Prereq: Stages 1–2

The one idea this stage is built on

Framing

Every compute service any cloud sells is a position on a single dial: how much undifferentiated heavy lifting you hand the provider. Turn the dial one way and you get control — kernel modules, GPU drivers, a filesystem you own. Turn it the other way and you get less toil — no patching, no capacity planning, no 3 a.m. node replacement — but you accept the platform's rules and a completely different cost curve.

Undifferentiated heavy liftingterm · used throughout

Work your users would never pay for and your competitors also have to do: racking servers, patching kernels, replacing dead disks, installing NVIDIA drivers, writing a scale-out script. It is real work; it is just not your work. Every rung up the ladder is a decision to stop doing some of it.

There is no "right" rung. There is only a rung that matches the shape of your workload. That is the second idea, and it is the one that shows up on your bill.

The abstraction ladder

Click any rung
more control less toil Dedicated host / bare metal you own the physical box 15% Virtual machine an OS you patch, an instance you name 35% Managed VM group fleet, health checks, autoscaling 50% Managed Kubernetes control plane theirs, nodes still yours 65% Serverless containers you bring an image; no nodes exist for you 82% Functions you bring a handler; the platform owns the loop 93%
Teal fill = share of the operational surface the provider absorbs. The percentages are illustrative, not measured — they encode the direction of the dial, not a benchmark.

Two more ideas you'll carry into every tab

Framing
Idea 2 · match compute to workload shape

Steady baseline → reserved or committed VMs. You know you need it; buy it cheaper.
Bursty with a known floor → an autoscaling group or a container platform. A warm minimum plus elastic headroom.
Event-driven and spiky, with real idle gaps → serverless. Pay per invocation, pay nothing at 3 a.m.

The villain of this stage is paying for idle: an always-on fleet sized for a peak that happens twice a day. Its quieter twin is choosing too much control — running a Kubernetes cluster to serve one stateless API the platform would have run for free.

Idea 3 · statelessness is the hinge

Autoscaling only works if a replica can be created and destroyed without anyone caring which one they got. That requires the application to keep no durable state in the process. Domain state goes to a database; session state travels in the request.

Say it plainly: state belongs in the storage tier — Stage 5 — not on the instance. Without that discipline, every rung above "single VM" quietly degrades into a single VM with extra steps: sticky sessions, replicas that can't be killed, a scale-in event that loses a shopping cart.

How the four sessions build

Map · click to jump

Each session runs the same five beats: why it existscore concepts (three passes, vendor-neutral first, then the three clouds) → reality check (numbers on paper plus cited current sources) → apply it (your GenAI context, one optional lab) → bridge.

What you already have, and what this stage adds

Continuity

Carried forward from Stages 1–2

  • The shared-responsibility line. Stage 1 taught it as IaaS / PaaS / SaaS. Compute is where that line becomes a purchase decision — the ladder above is the shared-responsibility line, drawn as a dial.
  • Regions, zones, resource hierarchy. Every fleet you build in this stage is placed against zones; a group that lives in one zone is a group that dies with one zone.
  • Identity. Every compute resource here runs as an identity — an execution role, a managed identity, a service account. When a tab says "attach the role," that is Stage 2 machinery, not new material.
  • The Rosetta-Stone habit. Keep translating. By the end of S4 you should be able to say "ASG ⇄ VMSS ⇄ MIG" without pausing.

What this stage is not

  • Not a Kubernetes course. You already run KServe. The tabs teach the cloud-specific wrapper — node pools, Autopilot, Auto Mode, Fargate profiles — not pods and deployments.
  • Not storage. Where state goes is Stage 5. This stage only insists, repeatedly, that it goes somewhere else.
  • Not networking. Load balancers appear as the thing an autoscaler registers instances with; their internals come later.
  • Not a price list. Prices in the reality checks are worked examples with dates attached. The method is the durable part; re-check the rate card before you commit money.
End goal for this stage

Pick a compute abstraction from workload shape and defend the cost trade-off. Map EC2 / ASG / ECS / EKS / Fargate / Lambda to their Azure and GCP equivalents instantly. Reason about autoscaling policy, cold starts, spot interruption, and GPU selection well enough to argue with a colleague — and to answer the exam question.

Why this session exists

10%

Somebody sized a server for the worst Tuesday of the year, and then paid for that Tuesday 365 days running.

That is the oldest cost mistake in infrastructure and the cloud did not fix it — it just made it easier to commit at scale. Before you can pick between a VM, a container, and a function, you need to see the gap the cloud is actually selling you: the difference between capacity you provisioned and capacity you used.

00:00 12:00 24:00 capacity provisioned — sized for peak + headroom actual demand this gap is the bill
Traditional IT had two answers to this: scale up (buy a bigger box) or size for maximum (buy enough boxes for the worst case). Both leave the red region on the bill.

The cloud's answer is a third option — make the capacity line follow the demand curve — and the whole rest of this stage is machinery for doing that. But the machinery only works if your application will tolerate being replicated and killed at will. So we start there.

Core concepts

50% · vendor-neutral first

1 · The ladder, properly

Pass 1 — intuition

Think about getting a meal. Bare metal is a farm: you own the land and the animals, and dinner is entirely your problem. A VM is a rented kitchen: someone else maintains the building and the gas line, you still cook. A managed VM group is a rented kitchen with staff who notice when a burner dies and light a second stove when the queue grows. Managed Kubernetes is a commercial kitchen with a head chef's system — you still hire the line cooks. Serverless containers is a ghost kitchen: you hand over the recipe in a box, they cook it when orders arrive. Functions is a vending machine: you supply one snack, the machine handles everything else and refuses to sell anything bigger.

The analogy holds where it matters: as you go up, you lose the ability to do unusual things, and you stop paying for an empty kitchen.

Pass 2 — mechanism

Mechanically, each rung is a different answer to who owns the boundary. Below the boundary the provider automates; above it, you do.

RungThe provider's unit of workYour unit of workWhat "scaling" means here
Bare metala rack, a power feedhypervisor, OS, everythinga purchase order
VMa virtualised machine, bootedOS patching, runtime, app, capacityyou resize or clone it by hand
Managed VM groupidentical VMs from a template, kept alivethe image, the health endpoint, the policya number goes up; new VMs boot
Managed Kubernetesthe control plane; often the nodes toomanifests, resource requests, node pool shapepods scale, then nodes scale under them
Serverless containerseverything but your imagea container that listens on a portinstances appear per unit of concurrency
Functionseverything but your handlerone function signatureinvisible — one environment per concurrent event

The mechanism, in one config fragment — this is what "scaling" is, everywhere:

min_instances: 2        # the floor you pay for even at 3am
max_instances: 20       # the ceiling that stops a runaway bill
metric: cpu_utilization # the signal the controller watches
target: 60              # the number it steers toward

Every autoscaler on every cloud is those four lines plus timing knobs. Learn them once.

Pass 3 — trade-offs and where it breaks

Going up costs you control in specific, predictable places. Kernel modules and custom drivers stop being possible around the serverless-container rung. Long-running processes stop being possible at the function rung (AWS caps a single Lambda invocation at 15 minutes; Cloud Run caps a request at 60 minutes). GPUs are available on some rungs and flatly absent on others — AWS Fargate has no GPU support at all, which is why a serving stack ends up on EC2 or EKS nodes rather than "serverless containers" on AWS.

Going up also changes the cost curve's shape, not just its height. A VM has a high fixed cost and a near-zero marginal cost per request. A function has zero fixed cost and a meaningful marginal cost per request. Two lines with different slopes cross somewhere — and that crossing point, not a vendor's marketing, is the real decision. We compute one in the reality check below.

The ladder is bending. The clean story — "VMs are cheap at scale, functions are cheap when idle" — is being deliberately eroded by the vendors. AWS shipped Lambda Managed Instances in December 2025: Lambda functions running on EC2 capacity you specify, so you can apply EC2 pricing models to a serverless programming model. Google shipped Cloud Run worker pools and GPU support. Expect the middle of the ladder to keep filling in.

2 · Stateless applications — why this is the hinge

Domain statethe long-lived data

Data that outlives a single interaction and is shared by everyone: an account balance, a product record, a model registry entry.

Session statethe per-user, per-conversation data

Data that only makes sense for one client across a series of requests: who they are, what's in the cart, which step of the wizard they're on.

Pass 1 — intuition

A stateless application is not an application with no state. Every program more complicated than a calculator has state. A stateless application is one that keeps its state somewhere else — domain state in a database, session state passed in with each request.

The analogy: a good hotel concierge is stateless. They don't remember you; your key card tells them your room, and the property management system tells them the materialing. That's exactly why any concierge on shift can serve you, why the hotel can add a second one at Christmas, and why one going home mid-shift breaks nothing.

Pass 2 — mechanism
clients session state session state params stateless replicas all equivalent no data survives a request read/write backend services database — domain state cache — Redis, etc.
Three parties, one rule: the middle box holds nothing between requests. Session state lives left; domain state lives right. That is the entire pattern.

Inside a request the application is free to hold data — it loads what it needs from the database using the context in the request parameters, does the work, writes back, and returns. Each call to the API defines one logical transaction, and at the end of it the process flushes everything. Concurrent requests each get their own thread with their own temporary variables, so no two clients can see each other's data.

The refactor, in two lines of pseudocode — the whole change is one keyword:

class ProductManager:
    self.cache = {}                     # ← instance variable: STATEFUL, breaks replication
    def get(name): return self.cache.setdefault(name, db.get(name))

class ProductManager:
    def get(name): return db.get(name)  # ← temporary only: STATELESS, replicates freely
Pass 3 — trade-offs and nuance

Holding domain data in memory does make individual reads faster. Statelessness gives that up, and it buys five things back:

  • Startup time. No prefetch, so a new replica is useful in seconds instead of minutes. This is exactly why autoscaling feels responsive on stateless services and sluggish on stateful ones.
  • Memory ceiling. Replicas stop each hoarding their own copy of the same rows, so you fit more concurrency per gigabyte.
  • Consistency. Nothing can go stale, because nothing is duplicated.
  • Graceful shutdown. A stateless process is always safe to kill between transactions. That is what makes spot instances and rolling deploys viable.
  • Recovery point. A crash loses only in-flight transactions. A stateful process that persists hourly has an effective recovery point objective of one hour — every crash silently deletes up to an hour of user work.

Where it breaks: re-reading the same rows on every request can saturate the network path to the database. The fix is not to cache in the process — it is an external cache (Redis, Memcached), which keeps the application stateless while restoring the read speed. Similarly, if session state grows large, passing it on every request becomes the bottleneck; the answer is to design an API with few, small parameters — pass an ID, not an object graph.

Sticky sessions are the tell. If your load balancer must route a user back to the same instance, you have not externalised session state, and every rung above "single VM" will fight you. The twelve-factor formulation is blunt about it: sticky sessions violate the model and should never be relied on; session data belongs in a store with time expiry.

3 · Replicable applications — running reliably on unreliable parts

Pass 1 — intuition

Traditional IT made systems reliable by buying reliable hardware. That gets expensive fast — and it never reaches zero outages, because there are also planned ones. The cloud takes the opposite bet: assume everything fails, and make the application redundant rather than the machine. One vendor who took this seriously deliberately bought cheaper RAM with a higher failure rate; if a batch turned out too reliable, they assumed they were being overcharged.

Pass 2 — mechanism

A replicable application is one the platform can deploy many times over from the same package, where the copies don't know about each other and don't interfere. The platform's deployer runs repeatedly; each run produces an equivalent replica. Running many replicas instead of one big one solves both problems at once — scalability, because you get the capacity of several machines, and reliability, because when one machine dies only its replicas die.

The replicas coordinate only through shared backend services. That's the whole architecture.

Pass 3 — what actually blocks replication

Teams new to this usually say "our app won't work that way." The useful move is to hunt for the specific assumption that only one copy would ever exist. The recurring offenders:

  • Singletons. An object designed to have exactly one instance shared globally. Two replicas each build their own, which defeats the purpose. Electing one replica to own it works right up until that replica crashes holding the lock — then nothing works, including the replacement.
  • Block storage. A volume typically cannot be attached to more than one workload. Each replica creates its own, each knows only its own data, and when a replica dies its data is effectively lost. Use a shared database service instead.
  • Fixed IP addresses or hostnames. The first replica claims it; the rest can't start.
  • In-process locks and semaphores. They coordinate threads inside one process and are invisible to the process next door.

Note the theme: replicas almost always work fine internally. The breakage is in how they use external resources, under an assumption of exclusivity that was true on one server and is false on twelve.

Two connections worth holding onto. First, replicas are usually copies of a VM image or a container image that wraps the application package — which is why "containerise it" and "make it replicable" feel like the same project. Second, statelessness makes replication dramatically easier: when replicas are stateless they are equivalent throughout their lifetimes, so routing is trivial and, on scale-in, any replica is an equally valid one to kill.

4 · The autoscaling control loop

Every autoscaler is a thermostat. Walk the loop:

Step 1 of 6 — observe
A metric crosses a threshold

The controller samples an aggregate signal — average CPU across the group, requests per instance, queue depth, or a custom metric. Aggregate is the key word: the target is the average across all healthy members, not any single one. A traffic spike arrives; average CPU climbs from 45% to 78%.

Sampling interval matters. Five-minute metric granularity means up to five minutes of blindness before the loop even starts.

Step 2 of 6 — decide
The policy converts the signal into a number

Three policy families, everywhere: target tracking ("hold average CPU at 60%" — the controller does the arithmetic), step or rule based ("above 80% for 5 minutes, add 2"), and scheduled ("at 08:00 weekdays, floor of 10"). A fourth, predictive, forecasts from history and acts before the load arrives — it needs about a week of data and only pays off for cyclical traffic.

If several metrics are configured, the controller computes a recommendation per metric and takes the maximum — so the worst signal always wins.

Step 3 of 6 — act
New instances are created from a template

The group boots new members from an immutable template — machine type, image, disk, network, startup script, and the identity the instance runs as. This is why the template is the real artifact: replicas are only equivalent because they came from the same one.

Boot to "OS ready" is often under a minute. Boot to "my application is warm and serving" can be five, and for a model server loading weights, much longer.

Step 4 of 6 — join
Health checks gate entry to the load balancer

A new instance does not receive traffic until it passes a health check a configured number of consecutive times. Two dials define "aggressive": the check interval and the healthy/unhealthy thresholds. A shallow check (TCP connect succeeded) proves the VM booted; a deep check (HTTP 200 from /ready) proves the application works.

Health checks do double duty: they also detect an instance that is running but wedged, and trigger its replacement.

Step 5 of 6 — wait
Warmup and cooldown stop the loop oscillating

Two distinct timers, routinely confused. Warmup excludes a brand-new instance from the aggregate metric until it has settled — otherwise a booting instance at 100% CPU makes the group look overloaded and triggers more scale-out. Cooldown blocks any further scaling action for a fixed window so the metric can respond to what you just did.

Set cooldown too short and you scale again before the new capacity is even serving. That is how a spike becomes a stampede.

Step 6 of 6 — scale in
Capacity is removed — carefully, and asymmetrically

Scale-in is deliberately more conservative than scale-out, because being slightly over-provisioned costs money while being under-provisioned costs users. Controllers add a stabilisation window (only shrink if the metric has stayed low across a trailing period) and a maximum reduction (never remove more than N instances or N% at once).

Then a termination policy picks which instance dies. With stateless replicas this is a free choice — which is the payoff for all that discipline earlier.

5 · Decision tree — VM, container, or serverless?

Follow "no ↓" down the left. A "yes" exits right. Bottom-left is the default.
Does the workload need kernel access, custom drivers, a specific GPU, or a licence tied to a host?
yes →
VM
single instance, or a managed group if you need more than one
no ↓
Does a single unit of work run longer than about 15 minutes, or hold an open connection for hours?
yes →
Container platform
functions are capped; containers are not
no ↓
Is traffic genuinely idle for long stretches — nights, weekends, or between events?
yes →
Serverless
functions for events, serverless containers for HTTP
no ↓
Do you already run Kubernetes, or need portability across clouds and on-prem?
yes →
Managed Kubernetes
you are paying for the ecosystem, not the scheduler
no ↓
default Serverless containers, autoscaling with a small warm minimum. A stateless HTTP service with steady-but-variable traffic and no exotic hardware needs is the single most common shape in the cloud, and it is the shape every provider's serverless-container product was built for. Start here; move down a rung only when a specific constraint forces you.

Note what is not in this tree: cost. Cost is a consequence of the shape, not an input to it — and once the shape is fixed, the purchasing model (on-demand vs spot vs committed) is where the real money is. That is a separate decision, taken per session below.

6 · The same ladder in three dialects

AWS
EC2

Virtual machines. IaaS, the VM rung.

EC2 Auto Scaling Group

A managed fleet of identical EC2 instances. The managed-VM-group rung.

ECS

AWS's own container orchestrator. Container rung, AWS-native flavour.

EKS

Managed Kubernetes. Container rung, portable flavour.

Fargate

Serverless compute under ECS or EKS — not an orchestrator itself.

Lambda

Functions. The top rung.

Azure
Virtual Machines

Virtual machines. IaaS, the VM rung.

Virtual Machine Scale Sets (VMSS)

A managed fleet of identical VMs. The managed-VM-group rung.

Azure Container Instances (ACI)

Single containers, no orchestrator. A rung of its own — simplest possible container.

Azure Kubernetes Service (AKS)

Managed Kubernetes.

Container Apps & App Service

Serverless containers, and managed web-app hosting.

Azure Functions

Functions. The top rung.

Google Cloud
Compute Engine

Virtual machines. IaaS, the VM rung.

Managed Instance Group (MIG)

A managed fleet from an instance template. The managed-VM-group rung.

Google Kubernetes Engine (GKE)

Managed Kubernetes, in two modes — Standard and Autopilot.

Cloud Run

Serverless containers. Services, jobs, and worker pools.

Cloud Run functions

Functions, built on Cloud Run. Formerly Cloud Functions.

App Engine

The original PaaS; still supported, rarely the new choice.

The three-word summary

ASG ⇄ VMSS ⇄ MIG. ECS/EKS ⇄ AKS ⇄ GKE. Fargate ⇄ Container Apps ⇄ Cloud Run. Lambda ⇄ Azure Functions ⇄ Cloud Run functions. If you can produce those four lines cold, you can navigate any of the three consoles. Everything in S2–S4 is detail hanging off them.

Reality check

25% · numbers on paper

Worked example — the same API, three shapes

The workload. A stateless HTTP API. 50 million requests per month. Average 200 ms of work per request. Diurnal traffic: mean 19 req/s, peak 77 req/s, trough 5 req/s. One 2-vCPU instance safely serves about 20 req/s of this workload. Reference instance price $0.10/hour (a general-purpose 2-vCPU class, us-east-1, mid-2026), 730 hours in a month, one load balancer at about $22/month.

ShapeSizingCompute+ LBMonthlyWhat you gave up
Always-on, peak-sizedceil(77 ÷ 20) = 4, +1 spare = 5 instances, 24/75 × 730 × $0.10 = $365$22$387nothing operationally — you just overpaid
Autoscaling groupmin 2, max 6, target tracking; time-weighted average ≈ 3.2 instances3.2 × 730 × $0.10 = $234$22$256~3 minutes of lag when a spike starts
Function, 1 GBno instances; 50M × 0.2s × 1GB = 10M GB-seconds(10M − 0.4M free) × $0.0000166667 = $160
+ (50M − 1M) × $0.0000002 = $9.80
$0*$170cold starts, a 15-minute ceiling, no GPU
$69
of compute actually consumed — 50M × 0.2s ÷ 4 slots per instance = 694 instance-hours
82%
of the always-on bill is idle. $387 spent to do $69 of work
~5%
apart — at this volume the autoscaled fleet and the function are nearly tied
$3.53
serverless marginal cost per million requests, vs ~$2.31 for a fleet held at 60% utilisation

Read the result honestly. The always-on fleet is the clear loser and that is the lesson of the session. But the interesting finding is the near-tie between the other two. Serverless has no floor and a higher slope; the fleet has a $168/month floor and a lower slope. Below roughly 50M requests/month, serverless wins outright. Above it, the fleet pulls ahead — but only if you keep it busy. The break-even is not a request count, it is a utilisation number: at this configuration the fleet stops winning once its average utilisation drops below about two-thirds.

* the asterisk that eats serverless savings

The $0 in the LB column assumes a direct function URL or an application load balancer at roughly $0.10 per million requests. Put an API gateway in front instead at a typical $3.50 per million and those 50M requests add $175 — more than doubling the total and flipping the comparison. On every cloud, the serverless bill is dominated by the things around the function: gateway, logging, egress, NAT. Price the whole path, not the compute line.

Three current sources worth reading in full

The Twelve-Factor App — factors VI, VIII, IX. The canonical statement of the rules above: processes are stateless and share-nothing; any data that must persist goes to a stateful backing service; processes are disposable and can be started or stopped at a moment's notice. the cloud patterns catalog quotes these directly, and every autoscaler on every cloud assumes them. → 12factor.net
CNCF Cloud Native Maturity Model 2.0. The corrective to treating this as a purely technical exercise. It frames cloud-native maturity across five dimensions — people, process, policy, technology, business outcomes — which is why "just lift the VM into the cloud" reliably fails to deliver the cost curve you expected. → maturitymodel.cncf.io
Warm pools, and the cost of slow boots. AWS's own documentation on Auto Scaling warm pools exists precisely because step 3 of the control loop above can be slow: pre-initialised instances sit alongside the group so a scale-out draws from them instead of booting cold. Practitioner reports put the improvement at roughly 3–5 minutes down to under 30 seconds for workloads with heavy initialisation — loading gigabytes of data, warming a JVM, or, for you, loading model weights. → AWS docs: warm pools

Apply it

10% · your context + one lab

Where you have already done this without calling it this

  • Your Modal deployment of Gemma via vLLM is the top of the ladder with a GPU bolted on. Scale-to-zero, per-second billing, no instances you name — that is the serverless-container rung. What you learned there transfers directly to Cloud Run GPU and Azure Container Apps serverless GPU; those are the same product shape from the hyperscalers. The thing to notice is what Modal hid from you: the cold-start cost of pulling an image and loading weights. In S4 we put a number on it.
  • Your Vertex AI script — T4 GPUs, autoscaling 1 to 3, spot instances — is the managed-VM-group rung. "Min 1, max 3" is exactly the min_instances / max_instances pair from the config fragment above. The min 1 is the important choice: you decided this endpoint keeps a warm floor rather than scaling to zero, which is the correct instinct for inference serving and the wrong one for a training job.
  • Spot for training, warm for serving is the workload-shape rule applied to GPUs, and it falls straight out of the statelessness discussion. A training job checkpoints — it tolerates being killed, so interruptible capacity is nearly free money. An inference endpoint holds no state either, but it holds a latency promise, and a two-minute eviction notice mid-request breaks that promise. Same statelessness, different constraint.
  • KServe already taught you the hard half. Readiness probes, replica counts, resource requests — that is steps 2 through 5 of the control loop. S2–S4 mostly teach you what each cloud calls those knobs.

Optional lab — model the crossover before you touch a console

Free · no resources created · 30 minutes

This session's lab is deliberately paper-only, because the concept is arithmetic and the console teaches nothing here.

  1. Take one service you have actually run — the vLLM Gemma endpoint is ideal. Write down: requests per day, average seconds of compute per request, memory needed, and the longest idle gap in a 24-hour window.
  2. Compute the three columns from the table above for your numbers. Use $0.0000166667 per GB-second and $0.20 per million requests for the function column (x86 rates, us-east-1, verified July 2026; Arm is about 20% cheaper).
  3. Find your crossover: solve floor + slope_fleet × x = slope_serverless × x for x in millions of requests.
  4. Then check whether your longest idle gap is long enough for scale-to-zero to matter. If your traffic never drops below 30% of peak, serverless is buying you very little and you should be shopping for a committed-use discount instead.

If you want to touch something anyway: open your cloud's pricing calculator and set a budget alert on your account before the next session — GCP budgets, AWS Budgets, Azure budgets. Every subsequent lab in this stage assumes one is already in place. Set it at a number that would annoy you, not a number that would ruin you — the point is to find out early.

Bridge to S2

You now have the ladder, the hinge that makes it work, and the arithmetic that decides between rungs. Next: AWS puts more rungs on that ladder than anyone — and names them all confusingly.
S2 turns "VM, container, serverless" into EC2, ASG, ECS, EKS, Fargate, and Lambda, and answers the question your GPU work actually needs: why vLLM serving is an EC2 instance and never a Lambda.

Why this session exists

10%

AWS sells six different ways to run the same container, and the difference between the cheapest and most expensive is roughly 5×.

AWS built its compute portfolio by accretion over twenty years, so the names encode history rather than logic. ECS and EKS are orchestrators; Fargate is not an orchestrator at all, it is a compute engine that runs underneath either one. Getting that one relationship wrong is the most expensive mistake teams make on AWS containers. This session gives you the map and, more importantly, the purchasing decision that sits on top of it.

A note on the material — read this first

§5 of the companion AWS reference is a broad services survey: §5.1 core services, §5.2 compute, §5.3 storage, §5.4 databases, §5.5 security, §5.6 workload selection, §5.7 deployment strategies. The compute portion is §5.2 only, and it is about three paragraphs per service — EC2, Lambda, ECS and EKS get a definition and a use-case list each. It contains no instance families, no purchasing options, no Auto Scaling Group mechanics, no Fargate, and no GPU coverage. The material is not wrong; it simply is not a compute section. Everything below the "what each service is" line is supplemented from current AWS documentation, and each such claim carries a link.

Core concepts

50%

1 · EC2 — reading the name is half the skill

Amazon EC2 (Elastic Compute Cloud)IaaS · the VM rung

Virtual servers you launch, size, patch, and pay for by the second. The foundation everything else on this page is built on — including, quietly, most of the "serverless" products.

Pass 1 — intuition

An EC2 instance type name is a compressed spec sheet. g6e.12xlarge is not a random string: it says G family (GPU, graphics and inference), 6th generation, e for enhanced memory, at the 12xlarge size point. Once you can read it, browsing 800 instance types becomes browsing about a dozen families.

Pass 2 — mechanism
g 6 e . 12xlarge family — g = GPU for graphics & inference generation — higher is newer silicon, usually better $/perf attributes — e enhanced memory · d local NVMe · n network · g Graviton · a AMD · i Intel size — large, xlarge, 2xl … 48xl; vCPU and memory scale roughly linearly
The families you will meet: T burstable M general purpose C compute optimised R/X/Z memory optimised I/D storage optimised G/P GPU Inf/Trn AWS silicon for inference and training F FPGA.

Two attributes matter more than the rest for your work. g means Graviton — AWS's Arm processors, generally cheaper per unit of work and now the default recommendation for anything that compiles cleanly on Arm. d means local NVMe attached to the host, which matters enormously for a model server that wants to cache weights on fast local disk instead of pulling from object storage on every cold start.

Pass 3 — the GPU lineup, verified July 2026

This is the part of any cert guide that ages fastest. Current state:

FamilyAcceleratorWhere it fits
G4dnNVIDIA T4, 16 GBStill available. Cheapest real inference GPU on AWS; fine for small models and for learning CUDA mechanics.
G5NVIDIA A10G, 24 GBThe previous inference workhorse; largely superseded by G6.
G6NVIDIA L4, 24 GBThe current default for single-GPU inference. g6.xlarge = 1× L4, 4 vCPU, 16 GiB.
G6eNVIDIA L40S, 48 GBWhen 24 GB of VRAM is not enough but you don't need an H100. g6e.xlarge gives ~45 GB usable.
P4d / P4deNVIDIA A100, 40/80 GBPrevious-generation training. Still widely used, increasingly a spot-capacity story.
P5 / P5e / P5enH100 (P5), H200 (P5e, P5en)Serious training and large-model inference, with Elastic Fabric Adapter networking for multi-node.
P6-B200 / P6-B300NVIDIA Blackwell B200 / B300P6-B200 went GA May 2025: 8 Blackwell GPUs, 1,440 GB of HBM, roughly 2× P5en for training and inference.
P6e-GB200 / GB300Grace Blackwell NVL72 UltraServersFrontier scale — up to 72 GPUs as one unit. Reserved through EC2 Capacity Blocks, not launched on demand.
Inf1 / Inf2 · Trn1 / Trn2 / Trn3AWS Inferentia and TrainiumAWS's own silicon. Cheaper per token if your stack tolerates the Neuron SDK; a real port for a vLLM shop, not a flag.

Sources: EC2 P5 instances P6 and P6e UltraServers Deep Learning AMI recommended GPU instances. Availability and pricing move monthly; check before you budget.

2 · Purchasing options — where the money actually is

Same instance, same performance, up to a 90% price difference. This is the highest-leverage decision on the page.

Per-second billing with a 60-second minimum, no commitment. Every other option is quoted as a discount from this. Correct for genuinely unpredictable workloads, for the first four weeks of any new service (you cannot commit sensibly without data), and for the 20–30% of your fleet you want to keep uncommitted.

Compute Savings Plans commit you to a dollar amount of hourly compute spend for 1 or 3 years and apply automatically across EC2, Fargate, and Lambda, across families and regions. Discount runs up to about 66%. This is the flexible option — it survives you migrating from x86 to Graviton, or from EC2 to Fargate.

EC2 Instance Savings Plans narrow the commitment to one instance family in one region. You keep flexibility on size, OS, and tenancy; the discount rises to about 72%. Roughly six percentage points deeper than the Compute plan — but it breaks the moment you switch families.

AWS generally steers customers to Savings Plans over Reserved Instances now.

Commit to a specific instance configuration for 1 or 3 years, up to about 72% off. Two reasons they still exist: Savings Plans do not reserve capacity, so a zonal Reserved Instance is how you guarantee a machine will be there; and Savings Plans do not cover RDS, Aurora, ElastiCache, or Redshift, which still need RIs.

Spare EC2 capacity at a discount of up to 90% versus on-demand. Prices move gradually with supply and demand rather than by auction. When AWS needs the capacity back, your instance gets a two-minute interruption notice and is then terminated.

That two minutes is the whole design constraint. It is generous for a training job that checkpoints every ten minutes. It is not enough for a stateful database, and it is uncomfortable for a latency-SLA inference endpoint unless you have warm capacity elsewhere. Spot instances also cannot receive Savings Plan or RI discounts — the two models are mutually exclusive.

Reserve GPU instances in advance for a defined block of time. This is how the frontier-scale hardware is actually consumed — P6e-GB200 UltraServers, for instance, are accessed through Capacity Blocks rather than launched on demand. If you have ever failed to get A100 capacity in a region, this is the answer AWS built.

A whole physical EC2 server for your exclusive use, purchasable on-demand or through Savings Plans. The reason is almost never performance; it is bring-your-own server-bound software licences, or a compliance requirement for physical isolation. This is the bottom rung of the ladder from Tab 0.

The one formula to remember

A commitment at discount d breaks even when your duty cycle exceeds (1 − d). A 1-year commit at 30% off pays for itself if you run the resource more than 70% of the hours — about 511 hours a month. A 3-year commit at 50% off needs only 50%. Everything else — payment options, coverage percentages, layering strategies — is refinement on that line.

3 · Auto Scaling Groups, end to end

EC2 Auto Scaling Group (ASG)the managed-VM-group rung

A logical fleet of EC2 instances that AWS keeps at a desired count, replaces when unhealthy, and resizes according to policy. Free itself — you pay only for the instances, EBS volumes, and load balancer.

launch template AMI · instance type IAM instance profile user-data · security groups versioned & immutable auto scaling group · min 2 / desired 4 / max 8 healthy healthy failed → replaced warming up health checks: EC2 status + ELB target + custom unhealthy → terminate → launch replacement warm pool — pre-initialised, stopped instances scale-out draws from here instead of booting cold load balancer registers only instances that pass the check scaling policies target tracking (thermostat) · step scaling (rules) · scheduled (clock) · predictive (7-day forecast, acts before the load)
The launch template is the real artifact. Everything else — health, scaling, replacement — is the group acting on copies of it.

Target tracking, the only policy most services need — this fragment is the mechanism:

TargetTrackingConfiguration:
  PredefinedMetricSpecification: { PredefinedMetricType: ASGAverageCPUUtilization }
  TargetValue: 60          # AWS creates and manages the CloudWatch alarms for you
EstimatedInstanceWarmup: 180   # seconds before a new instance counts toward the average

The timing knobs that decide whether it works

  • Default instance warmup. How long after reaching InService before an instance contributes to the aggregated metric. Not enabled by default — AWS explicitly recommends configuring it. Without it, a booting instance pegged at 100% CPU drags the group average up and triggers more scale-out.
  • Cooldown. A 300-second default pause after a scaling action. Note that target tracking ignores the static cooldown and uses warmup instead; step scaling honours it.
  • Detailed monitoring. One-minute metric granularity instead of five. Without it, you have added up to four minutes of blindness to every scaling decision.

The lifecycle features worth knowing by name

  • Lifecycle hooks pause an instance in Pending:Wait or Terminating:Wait so external automation can bootstrap or drain it.
  • Instance refresh rolls a new launch-template version through the group with a minimum-healthy-percentage target and rollback. This is how you deploy a new AMI.
  • Warm pools hold pre-initialised, stopped instances beside the group. They can hibernate, preserving memory state. You pay EBS storage for stopped instances but not compute.
  • Termination policies choose which instance dies on scale-in — default, oldest, newest, oldest launch template, or an allocation strategy for spot.

4 · Containers — the relationship that trips everyone up

Say this out loud once

ECS and EKS are orchestrators. Fargate is a launch type. The real question is two-dimensional: which orchestrator (ECS or EKS) and which compute underneath (EC2 instances you manage, or Fargate). "ECS vs EKS vs Fargate" is a malformed question, and it is why a 0.5-vCPU service can cost $18 or $91 a month depending on which boxes you tick.

ServiceWhat it isControl plane costThe catch
Amazon ECSAWS's proprietary container orchestrator. Task definitions, services, tight ALB/IAM/CloudWatch integration. No Kubernetes API.$0AWS-only. Small ecosystem — no Helm, no operators, no Istio.
Amazon EKSManaged upstream Kubernetes. Your manifests, Helm charts, operators, and GitOps tooling work unchanged.$0.10/cluster-hour ≈ $73/mo
$0.60/hr on extended support
You inherit Kubernetes. The fee is trivial at scale and absurd for one small service.
AWS FargateServerless compute engine under either orchestrator. Each task or pod runs in its own Firecracker micro-VM.n/a — per vCPU-second and GB-secondNo GPU support. No Arm on EKS Fargate. EKS pods capped at 4 vCPU / 30 GB. ALB only, private subnets only, no DaemonSets.
EKS Auto ModeGA since December 2024. AWS selects instance types, scales, patches the OS, and manages add-ons — Karpenter-style provisioning with an AWS-managed lifecycle.EKS fee + computeNewer, less escape-hatch than raw node groups. Closes most of the gap to ECS Fargate on operational effort.

Node scaling inside EKS is its own layer: the Horizontal Pod Autoscaler scales pods, the Vertical Pod Autoscaler resizes them, and underneath, Karpenter (now generally preferred over the older Cluster Autoscaler) provisions right-sized nodes in seconds and consolidates underutilised ones. KEDA adds event-driven scaling on queue depth. If you know KServe, you already know this layer — what is new is only that the nodes are EC2 instances in an ASG.

5 · Lambda — and its hard edges

AWS Lambdathe function rung

You supply a handler; AWS runs it in response to an event and bills per millisecond of execution. No instances exist from your point of view.

The limits that decide architecture

  • 15 minutes maximum per invocation. Hard limit.
  • 10,240 MB maximum memory. Hard limit. CPU scales with memory — this is the only performance dial.
  • 1,000 concurrent executions by default per region, raisable to tens of thousands. New accounts start lower.
  • 10,000 requests/second account limit, separate from concurrency and easy to hit first.
  • Burst of 500 concurrency per 10 seconds, or 5,000 requests/second per 10 seconds, whichever comes first.
  • 6 MB synchronous payload · 10 GB container image · 10 GB /tmp.
  • No GPU. None. There is no GPU resource type, no CUDA drivers, and no public roadmap commitment.

What changed at re:Invent 2025

  • Lambda Managed Instances. You define a capacity provider naming EC2 instance types, subnets, and scaling bounds; Lambda provisions, scales, patches, and routes to them. It exists because at steady high volume teams were re-architecting away from Lambda to reach bigger machines and Savings Plans. This is the ladder bending — serverless programming model, EC2 economics.
  • Durable Functions. Multi-step workflows with automatic checkpointing that can pause for up to a year waiting on an event or a human, with no compute charge while waiting. This deletes the main reason to reach for an external orchestrator on long AI workflows.
  • ECS Express Mode deploys a containerised app with one command, provisioning load balancer, autoscaling, networking, and domain.

AWS re:Invent 2025 announcements

cold start download code start runtime init code — imports, clients, model your handler ← billed warm skipped — the execution environment is reused your handler 0time Python ML functions commonly land at 3,000–4,500 ms of cold start — before inference even begins. Provisioned concurrency keeps environments initialised; you then pay for them idle, which is a VM by another name.
The init phase is where heavy dependencies hurt. This is also why a model server is a bad Lambda: weights load in init, and init runs again every cold start.

6 · Decision tree — which AWS compute for this workload?

Follow "no ↓" down the left. A "yes" exits right. Bottom-left is the default.
Does it need a GPU, a custom kernel, or more than 4 vCPU / 30 GB per unit?
yes →
EC2 in an ASG, or EKS on EC2 node groups
Fargate and Lambda are both out — neither offers GPU
no ↓
Does one unit of work exceed 15 minutes, or need a persistent connection?
yes →
ECS or EKS on Fargate
or Lambda Durable Functions if it is a workflow that mostly waits
no ↓
Is it event-driven with real idle gaps — queue messages, S3 uploads, scheduled jobs, sparse API calls?
yes →
Lambda
price the gateway and logs too, not just the function
no ↓
Do you need the Kubernetes ecosystem — Helm, operators, service mesh, GitOps — or multi-cloud portability?
yes →
EKS
Auto Mode unless you have a reason to manage node groups
no ↓
default ECS on Fargate. No control-plane fee, no nodes, no Kubernetes to learn, and it handles rolling and blue/green deployments natively. For a standard stateless web service or API on AWS, this is the answer that costs the least engineering time — and engineering time is the largest line item nobody puts on the invoice.

7 · Three-cloud comparison — AWS-anchored

ConceptAWSAzureGoogle Cloud
Virtual machineEC2 instanceAzure Virtual MachineCompute Engine instance
VM templateLaunch template (versioned)Scale set model / ARM templateInstance template (immutable)
Managed VM fleetAuto Scaling GroupVirtual Machine Scale SetManaged Instance Group
Discount for commitmentSavings Plans (66% compute / 72% instance) · RIs 72%Reserved VM Instances, up to 72% · Hybrid BenefitCUDs — resource-based to 55% (70% memory-opt), Flex 28%/46%
Automatic usage discountnonenoneSustained use discounts, up to 30% — no commitment
Interruptible capacitySpot, up to 90% off, 2-minute noticeSpot VMs, ~70–82% off, 30-second noticeSpot VMs, up to 91% off, ~30-second notice
Managed KubernetesEKS ($0.10/cluster-hr) · Auto ModeAKSGKE — Standard and Autopilot
Native orchestratorECS (free control plane)none — ACI is unorchestrated single containersnone
Serverless containersFargate (under ECS/EKS)Azure Container AppsCloud Run
Serverless GPUnot available — Fargate has no GPU, Lambda has no GPUContainer Apps serverless GPU — T4 and A100Cloud Run GPU — L4 and RTX PRO 6000
FunctionsLambda — 15 min, 10 GBAzure Functions — plan-dependent timeoutCloud Run functions — on Cloud Run, 60 min
Single-L4 inference VMg6.xlarge, 4 vCPU / 16 GiBNVads A10 v5 family (A10, not L4)g2-standard-4, 4 vCPU / 16 GB

Reality check

25%

Worked example — one L4 GPU, three ways to pay for it

Reference: g6.xlarge — 1× NVIDIA L4 24 GB, 4 vCPU, 16 GiB — at $0.8048/hour on-demand in us-east-1 (July 2026; regions and rates vary, verify before committing).

ScenarioArithmeticResultWhen this is right
Always on, on-demand730 h × $0.8048$587 / monthNever, if the workload is steady. This is the number every other option is trying to beat.
Training run, on-demand100 GPU-hours × $0.8048$80.48Short, urgent, can't tolerate interruption.
Training run, spot at 70% off100 h × $0.24 = $24
+ rework: 17 interruptions × 7.5 min avg lost = 2.1 h ≈ +5%
≈ $25Almost always, for training. 3.2× cheaper. Assumes you checkpoint every 15 minutes — the checkpoint interval is the risk control.
Serving, 24/7, 1-year commitbreak-even duty cycle = 1 − dcommit if you run >(1−d) of hoursAt d = 30%, you need 511+ hours/month. A 24/7 endpoint clears that trivially — so commit.
The silent money leak

A single g6.xlarge forgotten over a weekend: 62 hours × $0.8048 = $50. A p5.48xlarge (8× H100) forgotten the same weekend is roughly $2,500–4,000, depending on the rate you're paying. GPU instances do not idle cheaply — they idle at full price, because you are renting the silicon, not the utilisation. Every GPU lab in this stage ends with a teardown step, and you should treat that step as the most important line in the lab.

Why spot works for training and not for serving — the same fact, two conclusions

Both workloads are stateless in the Tab-1 sense. The difference is what they promise. A training job promises a result, eventually — so a two-minute eviction notice costs you the time since your last checkpoint and nothing else. An inference endpoint promises a latency percentile, continuously — and a correlated capacity reclaim can take several spot replicas at once, so "run two spot instances in two AZs" is weaker protection than it looks.

The production pattern that falls out: a committed or on-demand warm floor sized to your p50 traffic, with spot capacity layered on top for the peak. That is exactly the min/max pair from Tab 1, with a purchasing decision attached to each end.

Three current sources

EC2 purchasing options, from the source. AWS's own pricing page enumerates On-Demand, Savings Plans, and Spot, plus On-Demand Capacity Reservations and Capacity Blocks for ML. Worth reading because the distinction between a discount (Savings Plans) and a capacity guarantee (Capacity Reservations, Capacity Blocks) is the one most teams miss until a launch fails. → aws.amazon.com/ec2/pricing
Lambda scaling and concurrency. The docs walk through the concurrency formula — concurrency = requests/second × average duration — and then show why a function at 20 ms average duration and 30,000 req/s has a concurrency of only 600 but still breaks the 10,000 requests/second account limit. Two independent ceilings; most people only plan for one. → Understanding Lambda function scaling
Warm pools for slow-booting workloads. AWS's stated use case is applications with time-consuming initialisation — "loading gigabytes of data, provisioning services, or running custom scripts" that take minutes before an instance can serve. That is a verbatim description of loading model weights, which makes warm pools the single most under-used ASG feature for GPU serving. → AWS docs: warm pools

Apply it

10%

Why your vLLM serving is an EC2 instance and never a Lambda

Four independent disqualifications, any one of which is fatal:

  1. No GPU. Lambda has no GPU resource type, no CUDA drivers, and no billing dimension for one. This is not a limit you can raise.
  2. Memory ceiling. 10,240 MB is host memory, and it is less than the weights of most models you'd bother serving — before you consider that there is no VRAM at all.
  3. The 15-minute wall. Fine for a request; useless for a persistent server holding a KV cache and batching across requests. Continuous batching — the entire reason vLLM is fast — requires a long-lived process that sees many requests at once.
  4. Cold-start economics. Weight loading would land in the init phase and re-run on every cold start. Python ML functions already hit 3,000–4,500 ms of cold start without a model; add weights and the number becomes unbearable.

The AWS-native shape for your workload is therefore: a GPU instance in an Auto Scaling Group behind an application load balancer, with min = 1 (your warm floor — the same choice you already made on Vertex), a target-tracking policy on a custom metric (queue depth or tokens-in-flight, not CPU — CPU is meaningless on a GPU server), a generous instance warmup covering model load time, and a warm pool so scale-out doesn't pay the weight-loading cost twice. If you want Kubernetes semantics, the same thing on EKS with GPU node groups and KServe — which is closest to what you already know.

The Modal comparison is instructive. Modal gave you scale-to-zero GPU serving, which AWS does not sell: Fargate has no GPU, Lambda has no GPU. On AWS the closest equivalents are SageMaker asynchronous or serverless inference endpoints, or building it yourself with an ASG that scales to zero and eating the cold start. This is a genuine capability gap, and it is why Cloud Run GPU (S4) and Container Apps serverless GPU (S3) will feel more familiar to you than anything in this tab.

Optional lab — build the mechanism on a CPU instance

Free tier · ~45 minutes · teardown is mandatory

Do not use a GPU instance for this. The mechanics of an ASG are identical on a $0.0104/hour t3.micro and a $0.80/hour g6.xlarge, and you learn nothing extra from the expensive one. GPU instances are not free-tier eligible and cost several dollars per hour.

  1. Before anything: confirm your AWS Budget from S1 exists and is set low. AWS Budgets
  2. Create a launch template with t3.micro (or t4g.micro for Arm), Amazon Linux 2023, and user-data that installs a one-line HTTP server returning the instance ID.
  3. Create an ASG: min 1, desired 1, max 3, across two subnets in different AZs. Attach an application load balancer target group.
  4. Add a target-tracking policy at 40% average CPU, and set EstimatedInstanceWarmup to 120 seconds. Enable one-minute detailed monitoring.
  5. Drive load — ab, hey, or a shell loop with stress-ng on the instance. Watch the group scale out, then watch how long the new instance takes to appear in the load balancer as healthy. That gap is the number that matters; it is what a warm pool exists to shrink.
  6. Stop the load. Time the scale-in. Notice it is deliberately slower.
  7. Teardown, in this order: delete the ASG (this terminates the instances — deleting instances directly just makes the ASG replace them), then the load balancer, then the target group, then the launch template. Finally check the EC2 console for orphaned EBS volumes and Elastic IPs, which survive instance termination and keep billing.
Bridge to S3

AWS gave you the richest set of rungs and the least helpful naming. Azure inverts that: fewer compute primitives, clearer names, and one genuinely hard decision.
S3 covers VMs and Scale Sets — including the cooldown and flapping arithmetic AWS glosses over — and then the choice that dominates Azure architecture reviews: App Service, Functions, or Container Apps.

Why this session exists

10%

Azure will happily host your web app in four different services, and three of them are defensible.

Azure's compute story is cleaner than AWS's at the VM layer and messier at the PaaS layer. There is exactly one VM service and one scale-set service — no ECS/EKS-style fork. But above that sit App Service, Functions, Container Apps, and Container Instances, all of which will run an HTTP workload, and choosing between them is the single most common Azure architecture question. This session teaches the VM machinery properly (the Scale Set autoscaling model is the best-documented of the three clouds) and then resolves the PaaS choice with a decision tree.

Where the material has aged — three specific corrections

1. The GPU VM table is a decade out of date. §5's Table 5.1 illustrates the ND family with Standard_ND12s and Standard_ND24s — those are NVIDIA P40-era SKUs. The current lineup is NC A100 v4 (A100 PCIe 80 GB), NCads H100 v5 (up to 2× H100 NVL 94 GB), NCCads H100 v5 (confidential computing with GPU), ND H100 v5 (8× H100 SXM with InfiniBand), ND H200 v5, and announced GB200-class ND v6. Treat the material's Table 5.2 pricing as illustrative of shape only.

2. Flex Consumption is not in preview. The material's hosting-plan table marks it "In Preview at the time of writing." Flex Consumption went generally available in December 2024. It is now the plan you should reach for by default on new Functions projects.

3. NCv3 is gone. The V100-based NCv3 series retired on 30 September 2025; the migration guidance points at NVads A10 v5 or NCads H100 v5. NCv2 (P100) retired back in 2023. If a practice exam offers you an NCv3 answer, it is testing an old syllabus.

What the material gets right and is worth reading closely: the VMSS cooldown, flapping, orchestration-mode, and scale-in-policy material in §5 is the most precise treatment of autoscaling timing in any of your four sources.

Core concepts

50%

1 · Azure VMs and the SKU-name decoder

Azure Virtual MachineIaaS · the VM rung

On-demand virtual CPU, RAM, disks, and NICs. Same rung as EC2 and Compute Engine; different naming convention, and Azure's is the most information-dense of the three.

Pass 1 — intuition

Azure SKU names look worse than they are. Standard_DC8ads_v4 reads like line noise until you learn that it is a sentence with a fixed grammar — and once you have the grammar, you can read a SKU you have never seen and know roughly what it costs and why.

Pass 2 — mechanism
Standard_ D C 8 ads _v4 family — D general purpose · F compute · E memory · L storage · N GPU · H HPC · B burstable sub-family — C = confidential computing vCPU count — 8 additive features — a AMD CPU · d local temp disk · s premium storage support version — v4, v5, v6; higher is newer hardware
The full grammar: [Family][Sub-family]*[#vCPUs][Constrained vCPUs]*[Additive features][Accelerator type]*[Version]. Starred parts are optional. Azure VM sizes overview

Constrained vCPUs is the one piece with no AWS or GCP equivalent worth knowing: Azure lets you buy a large VM and disable some of its cores, so you keep the memory and I/O bandwidth of the big SKU while paying less in per-core software licensing. It exists almost entirely for Oracle and SQL Server licensing, and it is a favourite exam question.

Pass 3 — pricing models and where they differ from AWS
ModelShapeNotes and traps
Pay-as-you-goHourly, no commitmentThe reference price. Short-term or unpredictable workloads.
Reserved Instances1 or 3 years, up to 72% offYou pay whether or not you use it. The material states this bluntly and it is worth internalising — unlike AWS Savings Plans, which are dollar-denominated and flow to whatever you run, an Azure reservation is closer to a classic RI. Same break-even formula: commit when duty cycle > (1 − discount).
Spot VMsUnused capacity, roughly 70–82% off30 seconds of eviction notice, not AWS's two minutes. That is a quarter of the warning, and it changes what "checkpoint frequently" means in practice.
Hybrid BenefitApply existing Windows Server / SQL Server licencesNo AWS or GCP equivalent at this scale. If your organisation is already a Microsoft licensee, this can dominate every other lever.

The current GPU lineup, which the material's table predates: NC-family for applied AI training and batch inference — NC A100 v4 (up to 4× A100 PCIe 80 GB), NCads H100 v5 (up to 2× H100 NVL 94 GB), NCasT4_v3 (T4). ND-family for large distributed training — ND A100 v4, ND H100 v5 (8× H100 SXM, NVLink and InfiniBand), ND H200 v5. NV-family for visualisation and lighter inference — NVads A10 v5, and L40S-based sizes. One operational fact that catches everyone: every Azure subscription starts with a GPU vCPU quota of zero in every region, and approval for popular series can take days.

2 · Virtual Machine Scale Sets — the autoscaling model, properly

Virtual Machine Scale Set (VMSS)the managed-VM-group rung

A group of identical VMs that Azure creates, monitors, and resizes automatically. Direct equivalent of an AWS Auto Scaling Group and a GCP Managed Instance Group.

Orchestration mode — pick this correctly at creation, it is not changeable later

Uniform modeFlexible mode (recommended)
Best forLarge fleets of identical, stateless VMsHigh availability, mixed VM types, most new work
VM homogeneityAll identical, from one profileIdentical or mixed — different sizes, spot and on-demand together
Individual VM controlLimited; managed as a group via Scale Set VM APIs. Not compatible with standard Azure IaaS VM APIs.Full control using the standard IaaS VM APIs, like regular VMs
Visibility in the portalAbstracted inside the scale setVMs appear as individual resources
Instance ceiling1001,000

Why Flexible matters for GPU work: mixed VM types in one scale set means you can express "one on-demand instance as my warm floor, up to five spot instances on top" as a single resource — the exact serving pattern from S2, but native rather than assembled.

Scaling policies and the timing knobs

  • Metric-based. Thresholds on CPU, memory, network I/O, or custom metrics. "Scale out by one when average CPU exceeds 70% for a duration."
  • Schedule-based. Fixed instance counts at fixed times, for known patterns.
  • Predictive autoscale. Machine-learned forecast of CPU load from history, scaling out ahead of a predicted spike. Needs a minimum of seven days of history and only pays off for cyclical patterns.
  • Scale-in policy — which VM dies. Default balances across availability zones and fault domains, then deletes the highest instance ID; NewestVM and OldestVM do what they say.
  • Cooldown. Minimum wait after any scaling action before another can occur — in either direction. "If cooldown is 10 minutes and the set just scaled out, it will not scale again for 10 minutes." Set it too low and you scale again before new VMs are operational.
  • Threshold margin. Azure's own guidance: leave adequate space between scale-out and scale-in thresholds — for example out at 80%, in at 60%.
  • Same metric both directions. Using one metric to drive both rules avoids the conflicts that produce flapping.
  • Multi-instance steps. When scaling by more than one instance, autoscale may adjust the actual count to avoid a flap.

3 · Flapping — derive the rule instead of memorising it

Pass 1 — intuition

Flapping is when opposing scale events trigger each other in a loop: the group scales in, which raises load on the survivors, which triggers a scale-out, which lowers load, which triggers a scale-in. Nothing is broken and nothing is stable. The system spends its life booting and killing VMs, and your users get the latency of a permanent deployment.

Pass 2 — mechanism, with the arithmetic
unsafe — scale-in at 75%, scale-out at 80% 4 VMs @ 74% below 75% → scale in 3 VMs @ 74×4/3 = 99% above 80% → scale out 4 VMs @ 74% …and around again flap safe — scale-in at 60%, scale-out at 80% 4 VMs @ 59% below 60% → scale in 3 VMs @ 59×4/3 = 79% below 80% → stable
Removing one VM from a group of N multiplies per-VM load by N/(N−1). With N = 4 that is a 33% jump — enough to cross a 5-point gap instantly.

The rule, derived: a scale-in is safe only if the surviving instances stay below the scale-out threshold. That gives

scale_in_threshold  <  scale_out_threshold × (N − 1) / N

N = 4,  scale_out = 80%   →   scale_in must be < 60%     ← Azure's own recommendation, explained
N = 2,  scale_out = 80%   →   scale_in must be < 40%     ← small groups need enormous gaps
N = 10, scale_out = 80%   →   scale_in must be < 72%     ← large groups can be tuned tightly
Pass 3 — the consequence nobody mentions

Read the third line again. Small groups cannot be tuned efficiently. A two-instance scale set with an 80% scale-out threshold must not scale in above 40% — which means it spends most of its life running two instances at low utilisation, because removing one would immediately overload the other. The efficiency gains from autoscaling are structurally unavailable below roughly four instances.

This is a genuinely useful architectural finding, and it generalises to AWS and GCP unchanged — the arithmetic is not vendor-specific. If your fleet is two or three VMs, autoscaling is buying you availability, not savings. Price it accordingly, and consider whether a serverless platform that scales in units of one request would serve you better.

Now add the lag. Cooldown (say 10 minutes) plus VM boot and application initialisation (say 4 minutes) means up to 14 minutes between "load arrives" and "capacity is serving it." Against a 4× spike, you have three options and no fourth: over-provision the floor, accept 14 minutes of degradation, or use predictive autoscale to start before the spike. Most teams discover this during the incident rather than during design.

4 · AKS, and the PaaS layer above it

Compute-shaped
Azure Kubernetes Service (AKS)

Managed Kubernetes. Azure runs the control plane; you manage node pools — or let cluster autoscaler and node auto-provisioning do it.

Reach for it when: you already have Kubernetes manifests, need the CNCF ecosystem, or need GPU node pools. Note: Azure does not charge for the AKS control plane on the free tier — unlike EKS's $0.10/hour.

Azure Container Instances (ACI)

A single container, run directly, with no orchestrator at all. Its own small rung.

Reach for it when: you need one container for a burst job or a build agent and an orchestrator would be absurd. Also serves as burst capacity for AKS via virtual nodes.

Azure Batch

Managed job scheduling across a pool of VMs for large parallel and HPC workloads.

Reach for it when: the work is embarrassingly parallel and finite — rendering, simulation, bulk transcoding.

App-shaped
Azure App Service

Managed hosting for web apps and APIs. You deploy code or a container; Azure runs the web server, TLS, scaling, and deployment slots. PaaS, above the container rung.

Reach for it when: it is a conventional web application, you want deployment slots and built-in auth, and you don't want to think about containers at all. Scales up (bigger SKU) and out (more instances).

Azure Container Apps

Serverless containers with built-in service discovery, event-driven scaling (KEDA under the hood), and scale-to-zero. Direct peer of Cloud Run.

Reach for it when: you have containers and microservices but do not want a Kubernetes cluster. Consumption workload profiles scale to zero; Dedicated profiles run on reserved VMs you choose.

Azure Container Apps — serverless GPU

GA. NVIDIA A100 and T4, per-second billing, scale to zero when idle.

Reach for it when: you want Modal-style GPU serving inside Azure. Quota must be requested via a support case, and it runs on Consumption workload profiles only.

Event-shaped
Azure Functions

Functions. Triggered by HTTP, timers, queues, blobs, Event Hubs, and more; native support for C#, Java, JavaScript, PowerShell, and Python.

Reach for it when: the unit of work is genuinely an event. The hosting plan you pick matters more than the code.

Azure Logic Apps

Low-code workflow orchestration across connectors. Not really compute — it is glue.

Reach for it when: the job is integration between SaaS systems, not computation.

Azure Event Grid

Event routing. The thing that delivers events to the compute above.

Reach for it when: you need pub/sub fan-out between Azure services.

Functions hosting plans — this is the actual decision

PlanScaleCold startBillingWhen
Consumptionto 200 instances, event-drivenYes, on every idle gapPer execution + GB-second; scales to zeroSporadic work where latency on the first request doesn't matter. No VNet integration.
Flex Consumption
GA Dec 2024
to 1,000 instances, concurrency-based; 0→~1,000 in under a minuteAvoidable via always-ready instancesOn-demand instances billed only while executing; always-ready instances billed for idle memory tooThe default for new work. VNet integration at no extra cost, per-function scaling, 2,048 or 4,096 MB instance sizes, Azure Files mounts for large model binaries.
Premiumtypically 20–100 instancesPre-warmed instancesPer-VM, always at least oneLegacy answer to the same problems Flex now solves. Existing estates.
Dedicated (App Service plan)App Service scalingNone with Always OnPer App Service planYou already pay for an App Service plan and want to fill it.
On Container AppsContainer Apps scalingContainer cold startContainer Apps billingFunctions that need a custom container — and the only route to GPU-enabled Functions, via ACA serverless GPU.
Two traps that produce production incidents

The 230-second wall. The Azure load balancer has a 230-second idle timeout. Even if your function timeout is higher, an HTTP client gets a timeout error at 230 seconds. For anything longer, return 202 Accepted immediately and let the client poll. This catches every team that assumes the function timeout is the only ceiling.

"Always ready" is idle billing wearing a different hat. Flex Consumption bills always-ready instances for their provisioned memory while idle, plus execution time, plus executions. Setting always-ready to 2 gives you a warm floor and a monthly bill that no longer scales to zero — which is fine, as long as you decided it deliberately rather than discovering it. Also note the reported behaviour where the always-ready metric can exceed your configured count during load balancing and health checks, and you are billed for all active always-ready instances.

5 · Decision tree — hosting an application on Azure

Follow "no ↓" down the left. A "yes" exits right. Bottom-left is the default.
Do you need node-level control — GPU node pools, DaemonSets, custom kernels, a service mesh, or existing Helm charts?
yes →
AKS
with GPU node pools if the workload is inference or training
no ↓
Is the unit of work a discrete event — a queue message, a blob upload, a timer — rather than a long-lived service?
yes →
Azure Functions, Flex Consumption
add always-ready only if cold start hurts a user
no ↓
Do you have multiple containerised services that need to talk to each other, or event-driven scaling on queue depth?
yes →
Azure Container Apps
Consumption profile for scale-to-zero; add serverless GPU if you're serving a model
no ↓
Is it a single conventional web app or API that wants deployment slots, built-in auth, and zero container work?
yes →
Azure App Service
scale up for power, scale out for traffic
no ↓
default Azure Container Apps on a Consumption profile. If you have a container and no strong opinion, this is the rung that costs the least attention: no cluster, scale to zero, KEDA-based scaling on the trigger of your choice, and a straight upgrade path to Dedicated profiles or AKS if you outgrow it. Reach for a VM or a Scale Set only when something on this page told you to.

6 · Three-cloud comparison — Azure-anchored

Azure serviceWhat it isAWS equivalentGCP equivalentKey quirk
Virtual MachinesIaaS VMsEC2Compute EngineConstrained-vCPU SKUs for licence savings — unique to Azure
VM Scale SetsManaged identical VM fleetAuto Scaling GroupManaged Instance GroupUniform (100 VMs) vs Flexible (1,000, mixed types) — chosen at creation, permanent
Spot VMsInterruptible capacitySpot (2-min notice)Spot VMs (~30-sec notice)30-second eviction notice — the shortest of the three
AKSManaged KubernetesEKSGKENo control-plane charge on the free tier; EKS charges $0.10/hour
Container InstancesOne container, no orchestratorno clean peerno clean peerGenuinely useful for burst jobs; also backs AKS virtual nodes
Container AppsServerless containersFargate (under ECS/EKS)Cloud RunKEDA-based event scaling built in; Consumption vs Dedicated workload profiles
Container Apps GPUServerless GPU containersnoneCloud Run GPUA100 and T4; quota by support case; West US 3, Australia East, Sweden Central at GA
App ServiceManaged web-app PaaSElastic Beanstalk / App RunnerApp EngineDeployment slots and built-in auth are the differentiators
FunctionsFunctionsLambdaCloud Run functionsThe hosting plan, not the code, determines scale and cold-start behaviour
Hybrid BenefitReuse owned Windows/SQL licencesnothing comparablenothing comparableCan outweigh every other discount for a Microsoft shop
Predictive autoscaleML forecast of CPU loadPredictive scalingPredictive autoscalingAll three exist; all three need ~7 days of history and cyclical traffic

Reality check

25%

Worked example — sizing the lag budget for a 4× spike

Setup. A VMSS behind a load balancer. Scale-out at 80% CPU, scale-in at 60% (which the formula above tells us is correct for N ≥ 4). Cooldown 10 minutes. VM boot plus application initialisation: 4 minutes. Baseline 4 VMs at 50% CPU. At 09:00 traffic goes 4×.

tWhat happensUser impact
09:00Load 4×. Four VMs go from 50% to 200% demand — they saturate at 100% and start queuing.Latency climbing
09:00–09:05Metric aggregation and the configured duration window elapse before the rule fires.Degraded
09:05Scale-out triggers. New VMs begin booting.Degraded
09:09New VMs boot, initialise, pass health checks, and join the load balancer.Recovering
09:09–09:15Cooldown blocks further scaling — even if four VMs were not enough, no more can be added yet.Partially served
09:15Second scaling round if still needed.Served
~9 min
from spike to first new capacity serving
~15 min
worst case to full capacity, if one scaling round isn't enough
4 → 8
VMs you'd have needed pre-warmed to absorb the spike with no degradation
$X × 4
the monthly cost of that pre-warming — the number the business decision actually turns on

The three real options, and there is no fourth: (1) raise the floor so the baseline absorbs the spike — reliable, and you pay for four idle VMs every night; (2) accept nine minutes of degradation and communicate it as an SLO; (3) use predictive autoscale so the scale-out starts at 08:52 instead of 09:05 — free, but only works if the spike is genuinely cyclical and you have seven days of history proving it. Note that option 3 is the only one that improves both cost and latency, which is why it is worth the setup cost for any traffic pattern with a daily rhythm.

Three current sources

Azure Functions Flex Consumption, from Microsoft. The GA announcement documents concurrency-based scaling for both HTTP and non-HTTP triggers, scale from zero to 1,000 instances, VNet integration at no extra cost, and the always-ready feature. The instance graph in that post — 0 to nearly 1,000 instances in under 60 seconds — is the number to quote when someone claims serverless can't handle bursts. → Flex Consumption plan docs
Container Apps serverless GPU. Microsoft's GA post lists scale-to-zero A100 and T4, per-second billing, and "your data never leaves the container boundary" as the data-governance story — which is the argument for self-hosting an open model rather than calling a hosted API. Quota requires a support case, and Functions-on-Container-Apps is the route to GPU-enabled Functions. → Serverless GPUs in Container Apps
VMSS orchestration modes. Microsoft's comparison is the materiality on the Uniform/Flexible split, and the detail that matters most is API compatibility, not the instance ceiling: Uniform-mode VMs are not manageable through standard Azure IaaS VM APIs, which breaks a surprising amount of third-party tooling. Flexible is the recommended mode for new work. → Scale set orchestration modes

Apply it

10%

Mapping your existing stack onto Azure

  • Your Modal Gemma deployment → Azure Container Apps with serverless GPU. This is the closest thing Azure sells to what you already run: a container, an A100 or T4, per-second billing, scale to zero. The differences you'd feel immediately are the quota-by-support-case friction and the narrow regional footprint at GA. The architectural shape is identical — which means your Modal container image is close to portable, and the interesting work is in the scaling rules and the cold-start budget, not the model code.
  • Your Vertex AI script (T4, autoscale 1–3, spot) → a Flexible-mode VMSS with mixed VM types. Flexible mode is genuinely better than the AWS equivalent for this: one scale set expressing "one on-demand NCasT4_v3 as the warm floor, up to two spot instances above it." On AWS you assemble that from a mixed-instances policy; on Azure it is the mode's native purpose. But note the eviction notice is 30 seconds, not 120. If your Vertex spot strategy assumed a two-minute drain window, it does not transfer.
  • Your GKE/KServe knowledge → AKS. Almost everything transfers unchanged. What is new is the wrapper: node pools with GPU SKUs, the cluster autoscaler configuration, and the fact that you must request GPU quota before the first pod will ever schedule.
  • The identity link back to C2: where AWS gives an instance an IAM instance profile and GCP gives it a service account, Azure gives it a managed identity. Same idea, same purpose, different noun. Don't re-learn it — just add the third word to the Rosetta Stone.

Optional lab — a scale set that flaps, then doesn't

Smallest SKU · ~50 minutes · teardown is mandatory

Use Standard_B1s or Standard_B2als_v2 — burstable, cheapest, and eligible for the Azure free account's 750 free hours in the first 12 months. Do not use an N-series VM: GPU SKUs are not free-tier eligible, start at several dollars per hour, and require a quota request that will take longer than this lab.

  1. Before anything: create a budget with an alert at a low threshold. Azure budgets. Put everything in one new resource group — that is your teardown handle.
  2. Create a VMSS in Flexible orchestration mode: Ubuntu, Standard_B1s, instance count 2, min 2 / max 5, across availability zones, with a load balancer.
  3. Add a metric-based autoscale rule deliberately badly: scale out above 70% CPU, scale in below 65%, cooldown 5 minutes. Note that 65% violates the formula for N = 2 (which demands below 35%).
  4. Drive CPU with stress-ng --cpu 1 --timeout 900s on the instances. Watch the autoscale history blade. You are looking for the oscillation, not a graph that looks good.
  5. Fix it: scale in below 30%, cooldown 10 minutes. Repeat. Compare the two autoscale histories side by side — that comparison is the entire lesson.
  6. Optional: switch the scale set to include one spot instance and watch the eviction policy behave.
  7. Teardown: delete the resource group. This is Azure's one genuine ergonomic advantage over AWS — one delete removes the scale set, load balancer, public IPs, disks, and NICs together. Then confirm in Cost Management that daily spend returns to zero within 24 hours.
Bridge to S4

Azure gave you the clearest autoscaling model and the messiest PaaS menu. Google gives you the opposite: a tidy four-service ladder, and the most opinionated managed Kubernetes of the three.
S4 is also where this stops being theoretical — it maps directly onto the Vertex AI T4 deployment and the GKE footprint you already run.

Why this session exists

10%

This is the cloud you already run GPUs on — and the one whose pricing rules will surprise you most.

Google's compute ladder is the tidiest of the three: Compute Engine → Managed Instance Group → GKE → Cloud Run → Cloud Run functions, with no ECS/EKS-style fork and no four-way PaaS ambiguity. The complexity has been moved somewhere else — into the discount system. GCP is the only cloud with an automatic no-commitment discount, and it is also the cloud where that discount pointedly excludes the machine families you care about. Your Vertex AI T4 script sits exactly on that fault line.

Where the material has aged

§1's machine-series table stops at the third generation (M3, C3, H3, C3D). Current Compute Engine adds C4, C4A, C4D, N4, and N4A for general purpose and compute, plus a completely rebuilt accelerator line — A3 (H100/H200), A4 (B200), A4X (GB200), A4X Max (GB300), and G4 (RTX PRO 6000 Blackwell) alongside the older A2 (A100) and G2 (L4).

§2's MIG quota figures are self-dated — the material says "as of writing this material in late 2023 / early 2024," 2,000 VMs regional and 1,000 zonal. Treat the relationship (regional quota > zonal quota) as durable and the numbers as needing verification in the console.

§6's Autopilot-vs-Standard framing predates the hybrid model. Since 2025 you can run Autopilot-mode workloads inside a Standard cluster via Autopilot ComputeClasses — the choice is no longer all-or-nothing at cluster creation.

What the material gets right and ahead of the curve: §7 already uses the current "Cloud Run functions" naming rather than "Cloud Functions," and §2's treatment of MIG health checks is the best in any of your four sources — see below.

Core concepts

50%

1 · Compute Engine — machine families and the naming grammar

Compute EngineIaaS · the VM rung

Google's virtual machines. Per-second billing with a one-minute minimum, and a naming scheme that is the most readable of the three clouds.

Three nested terms, and the material defines them precisely: a machine family is a curated set of hardware configurations for a workload class; a machine series is a generation within that family (N2 is newer than N1; M3 newer than M2); a machine type is a specific vCPU/memory point within a series — or a custom machine type, which some series support and which has no clean AWS or Azure equivalent.

g2 - standard - 4          a3 - highgpu - 8g
│    │          │           │    │          │
│    │          └ 4 vCPU    │    │          └ 8 GPUs
│    └ configuration        │    └ high GPU density
└ series (G2 = L4)          └ series (A3 = H100)

n1-standard-8 + 1× nvidia-tesla-t4   ← N1 is the exception: GPUs attach separately,
                                        so you tune the CPU-to-GPU ratio yourself
SeriesAcceleratorOn-demand, us-central1 (Jul 2026)Where it fits
N1 + T4NVIDIA T4, 16 GB~$0.35/hr GPU add-on, plus the N1 rateStill current. The cheapest real inference GPU on GCP, and the one your Vertex script uses. Also the only GPU family that earns sustained use discounts.
G2NVIDIA L4, 24 GB$0.70/hr (g2-standard-4)The default single-GPU inference VM. Models up to roughly 13B at FP16.
A2A100 40 GB / 80 GB$3.67 / $5.07 per hourFine-tuning and medium training.
A3H100 80 GB (also H200 on A3 Ultra)$88.48/hr for a3-highgpu-8g (~$11.06/GPU)Note: 1-, 2-, and 4-GPU A3 sizes must be created as Spot or Flex-start VMs. Only the 8-GPU size is available on demand.
A4 / A4X / A4X MaxB200 / GB200 NVL72 / GB300A4 ≈ $4.28/GPU/hrFrontier scale. Requires reserved capacity, no self-service CUD, and the Compute Engine SLA does not apply to A4X.
G4RTX PRO 6000 Blackwell, 96 GB$4.50/hr (g4-standard-48)Newest family. Visualisation, rendering, and inference that needs a lot of VRAM without H100 pricing.
N1 + P100NVIDIA P100End of support 15 September 2026. Confirms the correction carried forward from earlier stages: do not target P100.

Sources: Compute Engine GPU machine types Accelerator-optimized machine family VM instance pricing. Rates move; re-check before budgeting.

The discount system — this is where GCP differs most

MechanismDiscountThe catch that matters
Sustained use discounts (SUD)up to 30%, automatic, no commitmentOnly N1 and other eligible general-purpose series. A2, A3, G2, G4 and the accelerator-optimized families get nothing. For GPU devices the discount is computed per GPU model — 30% off one GPU running a full month.
Resource-based CUDup to 55% (70% memory-optimized), 1 or 3 yearsRegion- and family-specific. A us-central1 A100 commitment does not cover europe-west4.
Compute Flexible CUD28% (1-yr) / 46% (3-yr)Spend-based, not region-specific, and since 2024 it covers Compute Engine + GKE (including most Autopilot pods) + Cloud Run in one commitment. GPU instances are not eligible.
Spot VMsup to 91% off~30 seconds of preemption notice. Spot VMs cannot receive CUDs or SUDs — the models are mutually exclusive.
Dynamic Workload SchedulervariesQueues batch AI/HPC jobs and starts them when capacity frees up at a reduced rate. Non-time-critical training without preemption risk.
The finding that will change how you budget

Commitment discounts on GPU instances are shallow. A g2-standard-4 saves roughly 8% on a 1-year CUD and 11% on a 3-year — against 37% and 55% for a comparable n2-standard-4. The reason is structural: the accelerator dominates the instance price and Google prices GPU commitments conservatively.

Apply the S2 formula and the conclusion is stark. Break-even duty cycle = 1 − d; at d = 0.08 you must run the instance 92% of the year before a 1-year GPU commitment pays for itself. For GPU workloads, Spot beats CUDs almost every time the workload can tolerate interruption, and driving utilisation above 80% delivers more saving than any commitment.

2 · Managed Instance Groups — and the two-health-check rule

Managed Instance Group (MIG)the managed-VM-group rung

A group of identical VMs created from an instance template, kept at a target size, auto-healed when unhealthy, and autoscaled by policy. Equivalent to an AWS ASG and an Azure VMSS.

Pass 1 — intuition

A MIG does two jobs that people conflate: it keeps the right number of VMs alive (auto-healing) and it changes that number (autoscaling). Auto-healing is about correctness; autoscaling is about capacity. They use the same signal — health checks — for different purposes, and that is exactly where the interesting subtlety lives.

Pass 2 — mechanism, and the best insight in your GCP source
instance template machine type · image startup script service account managed instance group vm vm initialising health check A — for the load balancer AGGRESSIVE: short interval, low thresholds consequence of firing: stop sending traffic cheap to be wrong — converge fast health check B — for MIG auto-healing CONSERVATIVE: longer interval, higher thresholds consequence of firing: destroy and recreate the VM expensive to be wrong — tolerate transient failures load balancer probes from 130.211.0.0/22 35.191.0.0/16
Two health checks, deliberately tuned differently. Configure only one and you get either a load balancer that is too slow to shed a bad instance, or a MIG that recreates VMs over a two-second network blip.

This is the single best idea in the GCP source and it is missing from the AWS and Azure treatments. When a MIG is a load-balancer backend, configure two separate health checks. The LB check should be aggressive because its only consequence is routing traffic away — cheap, reversible. The MIG check should be conservative because its consequence is destroying and recreating a VM — expensive, and on a GPU node, minutes of model reloading.

Health-check tuning is four dials: check interval, timeout, healthy threshold (consecutive passes to mark a sick VM healthy), and unhealthy threshold (consecutive failures to mark a healthy VM sick). "Aggressive" simply means low values on all four. Health checks are a Google-managed service running inside Google's network, so your VPC firewall must allow 130.211.0.0/22 and 35.191.0.0/16 — the single most common reason a new MIG reports every instance as unhealthy.

One more rule with real operational bite: if the MIG intentionally stops a VM — during autoscaling, for instance — it will not then "repair" it. But it will repair a VM lost to hardware failure, a Spot VM preemption, a maintenance event where the VM isn't set to live-migrate, or a manual deletion. That is how a MIG of spot instances self-heals.

Pass 3 — the autoscaler's timing model

The MIG autoscaler exposes more timing control than either competitor, and each knob maps to something you already met:

  • Utilization-based policy on average CPU, HTTP load-balancing capacity, or Cloud Monitoring metrics. If several metrics are configured, the autoscaler computes a recommendation for each and scales to the maximum — the worst signal wins.
  • Schedule-based policy — up to 128 schedules per MIG, each with a capacity floor, start time, duration, and recurrence.
  • Initialization period — how long a VM takes to become fully useful, application included. This is AWS's instance warmup.
  • Stabilization period — used only for scale-in. Gives a noisy metric time to settle before VMs are deleted.
  • Maximum allowed reduction and trailing time window — a hard cap on how many VMs (absolute or percentage) can be removed over an observation window. AWS and Azure have nothing this explicit.
  • Predictive autoscaling — forecasts from history and scales out in advance. Google's own guidance: it works best when your initialization period exceeds two minutes and load varies on daily or weekly cycles. That is a precise, actionable trigger condition — and a model server loading weights clears the two-minute bar easily.

Regional vs zonal MIGs. A zonal MIG puts every VM in one zone; a regional MIG spreads them across zones in the region. Regional gives higher availability against a zonal failure, and — a detail worth remembering because it is counter-intuitive — a higher effective VM ceiling, because regional quotas are larger than zonal ones.

Stateful MIGs exist for workloads that genuinely cannot be stateless — they preserve disks and instance names across recreation. Read them as an escape hatch, not a pattern. Everything Tab 1 said about the cost of statefulness still applies; a stateful MIG just makes it survivable.

3 · GKE — the wrapper, not Kubernetes

You know Kubernetes. Here is only what GKE adds on top.

Autopilot modeStandard mode
You managePods. That is the whole list.Node pools, machine types, upgrades, OS images, node security
BillingPer pod resource request — vCPU, memory, storage. Hardware-specific workloads shift to node-based billing.Per node VM, regardless of how full it is
Cheaper whenUtilisation is low or variable — you never pay for empty node capacityNodes run consistently hot, roughly 70–80%
GPUsSupported — request nvidia.com/gpu in the pod spec via the Accelerator compute class; GKE provisions and manages driversFull range of GPU types, node-level access, custom drivers
Escape hatchesNo SSH to nodes, no privileged containers, no hostPath. DaemonSets and Confidential Nodes do work in 2026 — older comparisons say otherwise.Everything

The 2026 update that makes this less of a fork: you can now run Autopilot-mode workloads inside a Standard cluster using Autopilot ComputeClasses. GKE provisions and manages those nodes while the rest of the cluster stays under your control with your own node pools. You can even set an Autopilot ComputeClass as the cluster or namespace default. Requirements are real — Rapid release channel, GKE 1.33.1-gke.1107000 or later — but the architectural point stands: Autopilot vs Standard is now a per-workload decision, not a per-cluster one.

The three autoscalers, which you already know from KServe but under different names: HPA adds pods on CPU, memory, or custom metrics; VPA right-sizes each pod's requests; Cluster Autoscaler adds and removes nodes under them. Node auto-provisioning goes one step further and creates new node pools of shapes you never defined. For your work the important interaction is that HPA scales pods in seconds while the Cluster Autoscaler must boot a GPU node in minutes — so a GPU deployment's real scale-out latency is the node-provisioning time, not the pod-scheduling time.

4 · Cloud Run — serverless containers, and the GPU story

Cloud Runthe serverless-container rung

You give Google a container that listens on a port. Google runs it, scales it by concurrency, and bills per second. Three shapes: services (request-driven), jobs (run to completion), and worker pools (GA — long-running consumers with no HTTP endpoint, for queue and stream processing).

The configuration surface

  • Concurrency — requests handled simultaneously per instance. Default 80, maximum 1,000. This is the dial that has no AWS Lambda equivalent, and it is the main reason Cloud Run is cheaper than Lambda for HTTP work: one instance amortises across many requests.
  • Request timeout — default 300 s, maximum 3,600 s (60 minutes). Google recommends idempotency and resumability past 15 minutes, because long connections drop.
  • Memory — 512 MiB default, 32 GiB maximum, with minimum-CPU requirements that scale alongside it.
  • Max instances — default 100 per revision. Set it thoughtfully: 100 instances all opening database connections will destroy a 20-connection pool.
  • Min instances — default 0. Set to 1+ to eliminate cold starts, and accept that you are now paying for idle.
  • Billing moderequest-based (per request plus a higher per-second rate only while processing) or instance-based (no per-request fee, lower per-second rates, billed for the whole instance lifetime).

Cloud Run GPU — the thing you actually want

  • GA since June 2025, covered by the Cloud Run SLA. NVIDIA L4 (24 GB) and NVIDIA RTX PRO 6000 Blackwell (96 GB).
  • Scales to zero. Per-second billing. No reservation needed and no quota request — projects using L4 in a region for the first time are automatically granted 3 GPUs.
  • One GPU per instance. L4 requires at least 4 CPU and 16 GiB; RTX PRO 6000 requires at least 20 CPU and 80 GiB.
  • Instance-based billing is mandatory for GPU, and min-instances are charged at the full rate even when idle. There are no per-request fees.
  • Start-up: an instance with the GPU and drivers ready in approximately 5 seconds. Google's own measured cold-start time-to-first-token for gemma3:4b — including startup, model load, and inference — was about 19 seconds.
  • Zonal redundancy on by default; turning it off lowers the price in exchange for best-effort failover.
Cloud Run functionsthe function rung

Formerly Cloud Functions. Single-purpose event-driven handlers. Gen 2 is deployed as services on Cloud Run and uses Eventarc for triggers — which means the function rung and the serverless-container rung on GCP are now the same runtime with two front doors. That is a cleaner story than either competitor offers.

5 · Decision tree — which GCP compute?

Follow "no ↓" down the left. A "yes" exits right. Bottom-left is the default.
Do you need multi-GPU, InfiniBand-class interconnect, custom drivers, or node-level access — i.e. distributed training?
yes →
Compute Engine A3/A4, or GKE Standard with GPU node pools
plus Spot or Dynamic Workload Scheduler for the capacity
no ↓
Does one unit of work exceed 60 minutes, or need a GPU larger than a single L4 or RTX PRO 6000?
yes →
GKE, or a MIG of Compute Engine VMs
Cloud Run's request ceiling is 60 minutes and it allocates one GPU per instance
no ↓
Are you already running a Kubernetes control plane for other workloads, or do you need the CNCF ecosystem?
yes →
GKE
Autopilot mode unless a workload specifically needs node control — and you can now mix both in one cluster
no ↓
Is the workload a discrete event handler with no long-lived process — a Pub/Sub message, a storage trigger, a webhook?
yes →
Cloud Run functions
gen 2, which is Cloud Run underneath anyway
no ↓
default Cloud Run — a service, with min-instances set from your latency requirement. Containerised, autoscaling by concurrency, scale-to-zero available, GPU available, and covered by the same Flexible CUD as your VMs and GKE pods. On GCP the burden of proof is on not using Cloud Run. Set min-instances=0 for anything internal or batch; set it to 1 when a human is waiting on the first request.

6 · Master three-cloud comparison

Rung / conceptAWSAzureGoogle Cloud
VMEC2Virtual MachinesCompute Engine
VM templateLaunch templateScale set modelInstance template
Managed VM fleetAuto Scaling GroupVM Scale Set — Uniform or FlexibleManaged Instance Group — zonal or regional
Warm-up knobDefault instance warmupCooldown periodInitialization period
Anti-flap knobCooldown (ignored by target tracking)Cooldown + threshold marginStabilization period + max allowed reduction
Pre-warmed capacityWarm pools (stopped or hibernated)Premium plan pre-warmed instances (Functions)min-instances (Cloud Run); MIG has no direct peer
Managed KubernetesEKS + Auto Mode · $0.10/cluster-hrAKS · free control plane tierGKE Standard + Autopilot · mixable per workload
Node right-sizingKarpenterCluster autoscaler + node auto-provisioningCluster autoscaler + node auto-provisioning + ComputeClasses
Serverless containersFargate (under ECS/EKS)Container AppsCloud Run — services, jobs, worker pools
Serverless GPUnoneContainer Apps — A100, T4 (quota via support case)Cloud Run — L4, RTX PRO 6000 (auto quota of 3)
FunctionsLambda — 15 min hard cap, 10 GBAzure Functions — plan-dependentCloud Run functions — 60 min, runs on Cloud Run
Long-running workflowLambda Durable Functions — up to 1 year, no charge while waitingDurable Functions / Logic AppsWorkflows + Cloud Run jobs
Automatic discountnonenoneSustained use, up to 30% — not on A2/A3/G2/G4
Flexible commitmentCompute Savings Plan, to 66% — covers EC2, Fargate, LambdaReserved Instances, to 72% — VM-scopedFlexible CUD, 28%/46% — covers CE, GKE, Cloud Run; not GPUs
Deepest commitmentEC2 Instance SP / Standard RI, to 72%3-yr Reserved, to 72% (+ Hybrid Benefit)Resource CUD, to 55% (70% memory-opt)
InterruptibleSpot — to 90%, 2-min noticeSpot — ~70–82%, 30-sec noticeSpot — to 91%, ~30-sec notice
Reserve future GPU capacityCapacity Blocks for MLReserved VM Instances + quota requestFuture reservations / AI Hypercomputer; DWS for batch
Single-L4 VMg6.xlarge — $0.8048/hrclosest is NVads A10 v5 (A10, not L4)g2-standard-4 — $0.70/hr
Compute identityIAM role via instance profileManaged identityService account

Reality check

25%

Worked example — Cloud Run GPU vs a dedicated L4 VM

This is the exact decision behind your Modal-vs-Vertex instinct, with numbers on it. Workload: an L4-sized inference endpoint — a 7B-to-9B model at FP16, comfortably inside 24 GB of VRAM.

OptionRateMonthly if always onCost when idle
g2-standard-4 Compute Engine VM$0.70/hr on-demand730 × $0.70 = $511$0.70/hr — full price, forever
Cloud Run with 1× L4, instance-based billingGPU $0.0001867/s ≈ $0.672/GPU-hr, plus the mandatory 4 vCPU and 16 GiB
$1.05/hr active (verify current rates)
730 × $1.05 = $767$0 at min-instances=0

Break-even is a duty cycle, not a request count:

break_even_hours = VM_monthly ÷ serverless_hourly
                 = $511 ÷ $1.05
                 ≈ 487 active hours per month
                 = 487 / 730
                 ≈ 67% duty cycle
67%
duty cycle where Cloud Run GPU and an always-on L4 VM cost the same
~1.3 req/s
the same point in traffic terms, at 2 s per generation and concurrency 4
1.5×
what you pay per busy hour for the right to pay nothing at 3 a.m.
~19 s
measured cold-start time-to-first-token for gemma3:4b, from zero

Read it as a rule you can state out loud: below roughly 1.3 sustained requests per second, serverless GPU is cheaper; above it, the dedicated VM wins. And crucially, the crossover is high — 67% is a busy endpoint. Most internal tools, demos, batch scoring jobs, and pre-launch products sit far below it, which is why scale-to-zero GPU serving is genuinely the right default for that whole class of work.

The cold start decides it, not the cost

Nineteen seconds. If a human is waiting on the first token, scale-to-zero is unacceptable — and the fix is min-instances=1, which puts you back at roughly $767/month with none of the savings. The moment you set min-instances above zero on a GPU service, the dedicated VM is cheaper, because Cloud Run charges min-instances at the full rate while idle and its hourly rate is above the VM's.

So the honest decision rule is: interactive and busy → Compute Engine or GKE with a committed L4. Bursty, internal, or batch → Cloud Run GPU at min-instances=0. There is no configuration that gives you both, and recognising that is the whole skill.

Two costs that are not on the hourly rate

Persistent disks bill while the VM is stopped. Stopping a VM stops compute charges and not storage — only deleting the disk stops those. A 500 GB SSD dataset is a real monthly line item before a single GPU-hour.

Vertex AI adds a premium over raw Compute Engine rates. Treat the managed-ML layer as a service with its own margin, not as a thin wrapper over the same price list — which matters directly when you compare your Vertex endpoint against the same GPU on GKE.

Three current sources

Cloud Run GPU configuration. The materialitative page for the constraints above — one GPU per instance, L4 minimums of 4 CPU and 16 GiB, RTX PRO 6000 minimums of 20 CPU and 80 GiB, mandatory instance-based billing, min-instances charged at full rate when idle, and roughly 5-second start with drivers pre-installed. Read this before designing anything around serverless GPU. → GPU support for Cloud Run services
The GA announcement, with Google's own measurement. Google's blog reports the ~19-second cold-start time-to-first-token for a Gemma 3 4B model — startup, model load, and inference combined — alongside the SLA coverage and the initial five-region footprint. That single number is the most useful piece of data in this session, because it converts an architectural preference into a latency budget. → Cloud Run GPUs are now generally available
Sustained use discounts, and who doesn't get them. Google's documentation confirms 30% off a GPU running a full month, computed per GPU model — and that accelerator-optimized families are excluded. Combined with the shallow GPU CUD rates, this is why your N1+T4 choice is quietly well-optimised and an equivalent G2 or A2 deployment would not be. → Sustained use discounts

Apply it

10%

Your Vertex AI script, re-read as architecture

You wrote: T4 GPUs, autoscaling 1 to 3, spot instances. Four deliberate decisions, and the vocabulary from this stage now names all of them.

Your choiceWhat it is, in this stage's termsVerdict
T4 GPUsN1 with an attached accelerator — the cheapest inference GPU, ~$0.35/hr add-on, and the only GPU family eligible for sustained use discountsWell chosen, partly by accident. An equivalent G2/L4 deployment would be faster but would forfeit up to 30% of automatic discount.
min = 1A warm floor. You decided against scale-to-zero.Correct for an endpoint with a latency promise — the 19-second cold-start figure above is the justification you didn't have at the time.
max = 3A ceiling that bounds the bill during a runaway.Correct, and the habit most people skip. Three T4s is a knowable worst case.
spot instancesInterruptible capacity, ~30 seconds of preemption notice, ineligible for CUDs and SUDsQuestion this one. Spot on the serving floor means your warm minimum can vanish with 30 seconds' notice. Spot for the burst instances above the floor is fine. Spot for the min-1 replica is trading your latency promise for a discount you may not have priced.

The refinement worth making: keep min = 1 on standard (non-spot) capacity so the floor is real, and allow instances 2 and 3 to be spot. That is the pattern from every session in this stage — a committed or on-demand floor, interruptible headroom — and on GCP the MIG will auto-heal a preempted spot VM for you, since preemption counts as a repairable failure.

Your other two workloads

  • Modal's scale-to-zero GPU serving → Cloud Run GPU, almost exactly. Container in, per-second billing, scale to zero, ~5-second instance start with drivers ready, no quota request for your first three L4s. The one real difference is concurrency: Cloud Run gives you an explicit dial (default 80, max 1,000), and for LLM serving you want it low — a value like 4 or 8 matched to what your vLLM instance can batch without blowing the latency budget. Getting that number right is worth more than any instance-type decision.
  • Your GKE footprint. Two things to go and check today. First, whether your GPU workloads are in Standard node pools that could move to an Autopilot ComputeClass — you would stop paying for empty node capacity. Second, whether your cluster is covered by a Compute Flexible CUD: since 2024 one commitment spans Compute Engine, GKE (including most Autopilot pods), and Cloud Run, and it is not region-specific. If you are running steady CPU-side workloads across all three, a single Flex CUD at 28% or 46% is the highest-leverage thing on this page — on the CPU side. It will not touch your GPU spend.

Optional lab — a MIG with two health checks

Always-free tier · ~45 minutes · teardown is mandatory

Use e2-micro in us-central1, us-west1, or us-east1 — Google's Always Free tier covers one e2-micro-equivalent for the full month. Do not attach a GPU for this lab. GPU instances are not free-tier eligible, run from about $0.35/hour for a T4 to over $88/hour for an 8-GPU A3, and an idle GPU VM is the single biggest silent money leak in cloud. If you want to see a GPU boot, use one short Spot VM run and delete it in the same sitting.

  1. Before anything: confirm a budget with alerts at 50/90/100% on the billing account, and work in a dedicated project — deleting the project is your nuclear teardown.
  2. Create an instance template: e2-micro, Debian, and a startup script that installs a tiny HTTP server returning the hostname on / and 200 on /healthz.
  3. Open the firewall to 130.211.0.0/22 and 35.191.0.0/16 on your serving port. Do this deliberately and notice it — skipping it is why most first MIGs report every instance unhealthy.
  4. Create two health checks: hc-lb-fast (interval 5 s, timeout 5 s, unhealthy threshold 2) and hc-mig-slow (interval 30 s, timeout 10 s, unhealthy threshold 3).
  5. Create a regional MIG, size 2, with hc-mig-slow as the auto-healing policy and an initial delay of 120 seconds. Attach it to an HTTP load balancer using hc-lb-fast.
  6. The experiment: SSH into one instance and make /healthz return 500. Watch the load balancer drain it within seconds while the MIG waits. Restore it before the MIG's threshold trips — that gap is the entire point of two health checks. Then break it permanently and watch the MIG recreate the VM.
  7. Add a utilisation autoscaling policy (target 60% CPU, min 2, max 4) and set a stabilization period. Drive load and watch scale-out, then note how much more slowly scale-in happens.
  8. Teardown, in order: delete the forwarding rule and load balancer components, then the MIG (which deletes its VMs), then the health checks, then the instance template. Check Compute Engine → Disks for orphaned persistent disks and VPC network → IP addresses for reserved static IPs — both keep billing after the VMs are gone. If in doubt, delete the whole project.
Bridge out of C3

You can now place any workload on the ladder, name its service on all three clouds, and defend the purchase with a duty-cycle calculation. The gap you have been stepping around all stage is state: every rung above "single VM" assumed the data lives somewhere else.
C5 is that somewhere else — object, block, and file storage, and the databases your stateless replicas share. C4 sits between: the networking that puts a load balancer in front of everything you built here.

← C2The path
Next stage · C4 →genaipros · C3 · Compute & GPUsAI for Everyone ↗