Cloud AI / Operating in the Cloud
Matching capacity to demand without wasting GPU-hours.
Reviewed by Yuvaraj
Serving an AI model is not like serving a web page: a single GPU replica is expensive, slow to start, and idle most of the time when traffic is bursty. Autoscaling matches the number of running model replicas to live demand, and cost engineering decides how aggressively you trade latency for spend. At this level the job is to pick a scaling strategy and a scaling signal that keep GPUs busy without starving requests, because on a GPU bill, idle time is pure waste.
Horizontal autoscaling adds or removes identical model replicas behind a load balancer. The hard part is choosing the signal that triggers scaling. CPU utilization, the default for web services, is the wrong signal for GPU inference, because the CPU often sits idle while the GPU is saturated. Better signals measure the actual bottleneck:
| Signal | What it measures | Best for | Caveat |
|---|---|---|---|
| GPU utilization | % of GPU compute in use | Steady, throughput-bound serving | Lags; batching can mask a growing queue |
| Queue depth | Requests waiting to be served | Bursty traffic; scale-to-zero triggers | Requires a queue or router in front |
| Concurrency | In-flight requests per replica | Latency-sensitive APIs | Must tune target concurrency per replica |
| p95/p99 latency | Tail response time | Strict SLAs | Reactive, scales only after latency degrades |
Kubernetes exposes this through the Horizontal Pod Autoscaler on custom metrics, and event-driven tools like KEDA scale directly on queue depth.
Ask about this lesson, or about anything in AI. Answers cite the lessons they draw on.
Finished this lesson?
Mark it complete to earn XP, keep your streak, and schedule a review.
When traffic can drop to nothing, scale-to-zero removes every replica so you pay for no GPU at all. The cost is the cold start: the first request after an idle period must wait for a full replica to come up, and for a large model that wait is dominated by loading weights into GPU memory.
Cold starts commonly run from a few seconds to a minute or more. Mitigations include keeping a small warm pool, snapshotting loaded state, using faster model loaders, and right-sizing down to a smaller model.
A GPU processes a batch of requests almost as cheaply as a single one, so grouping requests raises throughput per GPU dramatically. Modern LLM servers use continuous (in-flight) batching: they add and remove sequences from the running batch every step instead of waiting for a fixed batch to fill. Per-request cost falls as throughput rises:
Batching increases the denominator, but pushing batch size too high grows latency, so there is a throughput/latency knee to tune rather than a free lunch.
GPU-hours dominate the cost of inference, and a GPU costs the same whether it runs at 5% or 95% utilization, so utilization is the lever. Three tactics attack it:
Take a low-traffic internal assistant that is actually used about 3 hours per business day. Assume one replica on a GPU priced at $2.00 per GPU-hour (illustrative price, real rates vary by provider, GPU model, and commitment).
Always-on (24/7):
730 GPU-hours/month x $2.00 = $1,460 / month
Scale-to-zero (~3 h/day):
3 h/day x 30 days = 90 GPU-hours/month
90 GPU-hours x $2.00 = $180 / month
Savings: $1,460 - $180 = $1,280 / month (about 88% cheaper)
The catch is latency. The always-on replica answers instantly. The scale-to-zero replica is down most of the day, so the first request after idle pays the full cold start, perhaps 30-60 seconds, before it responds. You are buying an ~88% cost cut with occasional cold-start delay for the unlucky first caller.
Common mistakes