Safe rollouts and GPU autoscaling
Two operations problems. The first: ship a new model or a new quantisation without hurting users, and get back fast if you do. The second: add GPUs when traffic rises, even though a new GPU server can take ten minutes to become useful. Both come down to the same habit: measure before you trust, and plan for the slow part.
Start here (no maths needed)
When you change the model behind an app, you try it on a few people first and keep the old one ready. When lots of people suddenly arrive, you add more computers, but new ones take a while to start, so you have to add them early.
Think of it like this
Your school canteen wants to try a new pasta recipe. It doesn't swap the whole menu on Monday. It serves the new pasta to one table first, watches faces and leftovers, then to half the room, then to everyone. The old recipe stays in the fridge, so if kids hate the new one, lunch goes back to normal in minutes. Now the lunch bell rings and the queue triples. The canteen can open more serving counters, but setting up a counter takes ten minutes: unpack trays, heat the food. If they wait until the queue is out the door, kids miss lunch. So a smart canteen starts setting up counters when it sees the bell is about to ring, and keeps one counter half-ready just in case.
How it works
- Test quietly. Send copies of real requests to the new model and compare its answers, without showing them to anyone.
- Try it on a few. Give the new model a small slice of real users, then a bigger slice, checking quality, speed and errors at every step.
- Keep the old one running. If any check fails, switch everyone back automatically.
- Scale early. Watch the queue, not just how busy the GPUs look, and start new servers before you need them, because they're slow to start.
- Have a plan B. If the queue still overflows, give shorter answers or use a smaller model for a while rather than turning people away.
Why robots care
Robot fleets get software updates too. A new driving or grasping model goes to a few robots first, and each robot keeps the old model so it can switch back. A robot can't "add more GPUs" while it's working, though. Its computer is fixed. So when it's overloaded it has to do less, not buy more.
Try it
- With a friend, time how long it takes to set up a board game from the box. That's your "cold start". Now imagine ten groups arrive at once. How many games would you need to set up before they walk in?
- In the demo further down, set Cold start to 2 minutes and watch dropped requests vanish. Put it back to 10 and try fixing it with Headroom, then with Warm pool. Watch what each fix costs in Replica-hours.
Words to know
- Canary
- A small share of real traffic sent to the new version first, to catch problems early.
- Rollback
- Switching back to the previous version when the new one misbehaves.
- Autoscaling
- Adding or removing servers automatically as traffic changes.
- Cold start
- The time from "start a new server" to "it's answering requests".
- Warm pool
- Spare servers kept ready, with the model already loaded, so they can join in seconds.
Going deeper: everything below is the full version, with some maths. On a first read, skim the headings and the "Why it matters for robotics" box, and come back for the rest later.
Problem 19 · Roll out a new model or quantisation safely, with rollback
What the interviewer is testing: whether you treat a model change like a risky deploy with measurable gates, and whether you know the LLM-specific ways a "small" change breaks things (quality drift, longer outputs, a changed chat template, a cold prefix cache).
The concepts
A model rollout is two questions. Is the new version at least as good? And if it isn't, how fast can we undo it? The first needs metrics with enough data behind them. The second needs the old version to still be running when you find out.
What counts as "the model". Pin everything that changes outputs under one immutable version ID: weights, quantisation recipe, tokenizer, chat template, default sampling parameters, maximum context, and the serving engine version. Many "the new model is worse" incidents turn out to be a changed chat template or a new engine release, not the weights. If you can't name the exact bundle, you can't roll back to it.
The stages.
- Offline evals. Run the fixed evaluation suite: task accuracy, safety and refusal tests, long-context retrieval, tool-call format checks. Set pass thresholds in advance (Module 01 covers thresholds). For a quantisation change, also compare against the unquantised model on a fixed prompt set: top-1 token agreement and the KL divergence between their next-token distributions.
- Shadow traffic. Mirror a copy of real requests to the new version and throw its answers away. You learn latency, memory use, error rates and output-length shifts under real load, with zero user risk. It costs extra GPUs for the duration.
- Canary. Route a small share of users (1%, then 5%, 25%, 50%, 100%) to the new version. Hold each stage for a fixed "bake time" and compare against the old version on the same time window, so daily traffic patterns cancel out.
- Automatic gates. At every stage, compare canary to baseline. Any gate breached means automatic rollback, not a meeting.
- Keep the old version warm until the new one has run at 100% for a full traffic cycle (at least a day). Then scale the old one down.
gates (canary vs baseline, same window):
errors: 5xx rate, timeouts, malformed tool calls
latency: p50 / p99 TTFT, p50 / p99 TPOT
quality: automated judge score on sampled traffic, thumbs-down rate,
refusal rate, mean output length
cost: GPU-seconds per request, KV-cache utilisation
rollback if any gate is worse than its threshold with enough samples
Routing details that matter for LLMs. Make assignment sticky per conversation. Switching models mid-conversation changes tone and can break tool-call formats. It also throws away the prefix cache, because cached keys and values belong to one exact model. New replicas start with a cold prefix cache, so their TTFT looks worse at first; compare after warm-up, or you'll roll back a good model. (Page 04 covers prefix caching.)
Rollback speed depends on where the old version is. If old replicas are still running, rollback is a router weight change: seconds. If you scaled them to zero, rollback is a cold start: minutes (see Problem 20). That's why you keep the old fleet warm during the canary, and why blue/green (a full second fleet) is the fastest rollback but costs up to double the GPUs during the switch.
Worked answer: how much traffic does a gate need?
Say the baseline thumbs-down rate is 5.0%, and you want to catch a regression to 5.5%. How many rated responses does each arm need, at a 5% false-alarm rate and 80% power? The standard two-proportion formula:
n per arm ≈ (z_α/2 + z_β)² × [p1(1−p1) + p2(1−p2)] / (p2 − p1)²
z_α/2 = 1.96 (α = 0.05, two-sided) z_β = 0.842 (power 0.8)
p1 = 0.050, p2 = 0.055
(1.96 + 0.842)² = 7.851
p1(1−p1) + p2(1−p2) = 0.0475 + 0.051975 = 0.099475
(p2 − p1)² = 0.000025
n ≈ 7.851 × 0.099475 / 0.000025 ≈ 31,240 rated responses per arm
Now plug in traffic. Assume a service with 2 million requests a day and a 5% canary: 100,000 canary requests a day.
- If an automated judge scores every canary response, 31,240 samples take about 7.5 hours.
- If only 2% of users ever click a rating, you need 31,240 ÷ 0.02 ≈ 1.56 million canary requests, about 15.6 days at 5%.
The lesson: user feedback is too sparse to catch small regressions quickly. Use offline evals and an automated judge on sampled traffic for fast gates. Keep user signals for slower, confirming checks. Tail latency has the same problem: to say anything about p99 TTFT you need thousands of requests, because only 1 in 100 lands in the tail.
Trade-offs
| Strategy | User risk | Extra GPUs | Rollback time | Catches |
|---|---|---|---|---|
| Offline evals only | None, but blind to real traffic | Small, temporary | n/a | Known task regressions |
| Shadow traffic | None | Up to a full copy while it runs | n/a | Latency, memory, errors, output-length drift |
| Canary (1→100%) | Small slice at a time | A few replicas | Seconds if old fleet is warm | Everything, given enough samples |
| Blue/green | All at once on switch | Up to 2× during switch | Seconds | Only what the switch reveals |
| Big-bang replace | Everyone | None | A cold start, minutes | Too late |
How to say it in the interview
I treat a model or quantisation change as a versioned bundle: weights, tokenizer, chat template, sampling defaults and engine version, under one ID. It passes offline evals with thresholds set in advance, including a logit-agreement check against the unquantised model. Then it takes shadow traffic to check latency, memory and output length under real load. Then a sticky, per-conversation canary at 1, 5, 25, 50 and 100%, with automatic gates on errors, p99 TTFT and TPOT, an automated quality score and cost per request. I size each stage so the gates have enough samples: catching a 5.0 to 5.5% thumbs-down shift needs about 31,000 rated responses per arm. The old fleet stays warm until a full day at 100%, so rollback is a router change that takes seconds.
Common mistakes
- Comparing canary to last week instead of to the baseline in the same time window.
- Judging after five minutes. Small regressions need thousands of samples; cold prefix caches make a new version look slow at first.
- Scaling the old version to zero on day one, so rollback becomes a ten-minute cold start.
- Checking only accuracy. Quantised models often fail as longer outputs, more refusals, broken JSON, or worse long-context recall.
Problem 20 · Design autoscaling for GPUs with slow cold starts
What the interviewer is testing: whether you know why CPU-style autoscaling fails for LLM serving, can break a cold start into parts and shorten each one, and can size spare capacity from the traffic ramp and the cold-start time.
The concepts
Where the minutes go. A new replica isn't ready until every step below finishes. The numbers are rough assumptions for a 70B model in FP8 (about 70 GB of weights), to show the shape. Measure your own.
| Step | Assumed time | How to shorten it |
|---|---|---|
| Get a GPU node from the cloud | 2–10 min, or never if the region has no capacity | Reserved capacity, a standby pool of nodes, several regions |
| Pull the container image (CUDA images are often 10–20 GB) | Tens of seconds to minutes | Pre-pulled images on the node image, smaller images, lazy-loading snapshotters |
| Fetch weights (70 GB) | About 35–70 s at an assumed 1–2 GB/s from object storage; about 14 s at an assumed 5 GB/s from local NVMe | Cache weights on local disk, stream straight to GPU, copy from a peer GPU |
| Engine start-up (memory profiling, CUDA graph capture, compilation) | Tens of seconds to minutes | Cache compiled artefacts, capture fewer graph sizes |
| Health check, warm-up requests | Seconds | Keep, but make it quick |
Even with every trick, a cold node is minutes away. A node already running with weights on local disk is tens of seconds away. A loaded, idle replica is a router change away. That ladder is the whole design space.
Scale on leading signals, not GPU utilisation. GPU utilisation says a kernel was running, not whether users are waiting. A continuously batched server can show high utilisation at light load and stay at the same number while its queue explodes. Better signals, all exported by vLLM, SGLang and TensorRT-LLM in some form (page 08 covers them):
- Waiting requests (queue depth) per replica.
- KV-cache utilisation. Near 100% means new requests can't be admitted, or running ones get preempted.
- Pending tokens: prompt tokens waiting for prefill plus expected output tokens. Requests vary 1,000× in cost, so tokens beat request counts.
- TTFT p95, as a lagging check that the others are working.
Size headroom from the ramp and the cold start. While a replica boots, traffic keeps growing. Spare capacity must cover that growth:
spare capacity needed ≈ traffic growth rate × cold-start time
replicas of headroom = ⌈ (dλ/dt × t_cold) / μ ⌉
dλ/dt traffic growth (requests/s per minute)
t_cold cold-start time (minutes)
μ requests/s one replica sustains at the latency target
queue growth while short of capacity: dQ/dt = λ − N·μ (N ready replicas)
The toolkit:
- Predictive scaling. LLM traffic is strongly daily and weekly. Scale up on a schedule before the morning ramp, and let reactive scaling handle the error.
- Warm pool. Keep k loaded, idle replicas (or nodes with weights on disk). They join in seconds. You pay for them while idle.
- Fast scale-up, slow scale-down. Add replicas as soon as a signal crosses its threshold. Remove them only after the signal stays low for a cooldown (say 10–15 minutes), and drain in-flight streams first. Otherwise you'll flap, paying a cold start every few minutes.
- Admission control and graceful degradation. When the queue exceeds what you can serve within the timeout, shed load deliberately: cap max output tokens, route free-tier users to a smaller model, return a fast "busy, retry" to batch jobs. A queue that grows without limit just turns every request into a timeout.
- Bin-pack across models. If you serve several models, a shared pool of nodes with weights cached locally lets you shift GPUs between models faster than buying new ones.
Worked answer: how much headroom for a spike?
Assumptions (all illustrative): baseline 200 requests/s; a launch spike takes traffic to 500 requests/s over 10 minutes; one replica sustains 25 requests/s at the latency target; a cold start takes 10 minutes; the autoscaler keeps 20% headroom over current demand.
starting replicas = ⌈200 × 1.2 / 25⌉ = ⌈9.6⌉ = 10 → 250 req/s capacity
growth rate = (500 − 200) / 10 min = 30 req/s per minute
growth unseen during one cold start = 30 × 10 = 300 req/s
replicas to cover it = 300 / 25 = 12
spare already running = (250 − 200) / 25 = 2
→ need about 10 more replicas ready the moment the ramp starts
(a warm pool of 10, or a schedule that adds them before the launch)
with no warm pool: demand passes 250 req/s about 2 minutes into the ramp.
New replicas arrive 10 minutes after they're requested.
Meanwhile the queue grows at up to 500 − 250 = 250 req/s.
The demo below runs exactly this scenario. With the defaults (spike 2.5×, cold start 10 min, headroom 20%, no warm pool), about 2.3% of requests are dropped, queue waits hit the 30-second timeout, and waits exceed 2 seconds for 13 minutes. A warm pool of 10 removes the drops entirely, but the replica-hours over three hours rise from about 55 to about 78, roughly 41% more GPU cost for that window. Raising headroom to 60% alone costs about 74 replica-hours and still drops 0.8%: headroom over current demand can't cover a spike that's 2.5× the baseline.
Interactive: autoscaling with a cold start
Three hours of traffic, one-minute steps. Thin line: demand (requests/s). Solid step line: capacity of ready replicas. Dotted line: capacity once booting replicas are ready. Red fill: demand above capacity, so the queue grows. Bars along the bottom: queue wait (red when over 2 s; the timeout is 30 s, after which requests are dropped). Assumed: baseline 200 req/s, 25 req/s per replica, 10-minute scale-down cooldown, warm-pool replicas join in 1 minute and are refilled after one cold start.
Trade-offs
| Lever | Cost | Helps with | Weakness |
|---|---|---|---|
| Static headroom | Paid all day | Small, fast bursts | Large spikes beat any sensible percentage |
| Warm pool | Idle GPUs | Sudden spikes | Must be refilled after use; sized by guesswork |
| Predictive schedule | Little, if the forecast is good | Daily and weekly ramps, planned launches | Blind to surprises |
| Faster cold starts | Engineering time, local disk | Everything | Node availability is outside your control |
| Load shedding / degrade | Some users get less | Anything that gets past the others | Visible to users; needs product agreement |
How to say it in the interview
I'd first break the cold start down (node, image, weights, engine start-up) and shorten each part: pre-pulled images, weights on local NVMe, cached compile artefacts. I scale on leading signals, meaning queue depth, KV-cache utilisation and pending tokens, not GPU utilisation, which stays high while queues explode. Headroom has to cover traffic growth during one cold start: at 30 requests/s per minute and a 10-minute cold start, that's 300 requests/s, or 12 replicas at 25 requests/s each. I get that from a predictive schedule for the daily ramp plus a small warm pool for surprises, scaling up fast and down slowly with draining. Past that, admission control sheds or degrades load on purpose, so overload means shorter answers for some users rather than timeouts for all.
Common mistakes
- Autoscaling on GPU utilisation or CPU, which don't track user waiting time for batched LLM servers.
- Counting requests instead of tokens. One 100K-token prompt costs as much as hundreds of short chats.
- Scaling down as fast as up. The system flaps and pays a cold start each time.
- Assuming the cloud always has GPUs to give you. At peak, "request a node" can fail; reserved capacity or a second region is part of the design.
Practise it
- Measure a real cold start. On a Colab GPU or any single GPU, time each phase of starting vLLM with a small model (for example a 0.5–1.5B instruct model): Python import, weight download, weight load, engine ready (the first successful request). Run it twice to see the effect of a warm disk cache. Then try
--enforce-eagerto skip CUDA graph capture, and note how start-up time and decode speed both change. - Build a canary gate. Serve the same small model twice, once in BF16 and once quantised (FP8 if your GPU supports it, otherwise a 4-bit checkpoint). Send the same 500 prompts to both. Record top-1 agreement on greedy decoding of the first token, mean output length, JSON validity on a structured-output prompt, and p50/p99 TTFT. Write the gate as code: thresholds in, pass/fail plus a reason out.
- Simulate the autoscaler. Port the demo's logic to Python. Replace the synthetic spike with a daily curve, add a predictive schedule that scales 15 minutes ahead of the forecast, and plot replica-hours against dropped requests for warm pools of 0 to 12. Find the cheapest setting with zero drops.
In this course
- Page 08 · Serving engines and observability: the metrics these gates and scaling signals are built on.
- Page 06 · Quantisation: how to measure the loss a quantised rollout must pass.
- Page 03 · Batching and scheduling: admission control and priority queues, the last line of defence in a spike.
- Module 01 · Decision-model evals: setting thresholds before you look at results.
- Module 05 · Serving: why request costs span about 1,000×, which is why tokens beat request counts as a scaling signal.
- Module 04 · Latency is a safety number: the robot side of "waiting too long".
Why it matters for robotics
- Fleet updates are canaries. Shipping a new policy or a quantised perception model to a robot fleet follows the same stages: simulation and offline evals, a few robots in controlled sites, then wider. Robots typically keep the previous model on board so they can roll back without a network connection.
- Gates are physical. Robot rollout gates include intervention rate, task success and near-miss counts, which are rarer than thumbs-downs. The sample-size arithmetic above says small fleets catch only large regressions quickly.
- A robot can't autoscale. Its onboard computer is fixed. Overload has to be handled by doing less: a smaller model, a lower planning rate, or a safe stop. Problem 20 itself applies only to the cloud side.
- Cloud back-ends for robots do face spikes. A fleet that starts a shift at 8:00 sends a synchronised burst to any shared planning or VLM service. That's a predictable ramp, so schedule capacity for it.
- Boot time is a robot cold start. Loading a model into an embedded GPU's memory after a power cycle takes real seconds. Measure it, because it decides how long the robot sits idle after a restart.
Self-check
Why keep conversations sticky to one model version during a canary?
Switching mid-conversation changes tone and can break tool-call formats, it mixes the two versions' quality signals, and it throws away the prefix cache, which only works for the exact model that produced it.
Baseline error rate 5.0%; you want to detect 5.5% at α = 0.05 and 80% power. Roughly how many samples per arm?
About 31,000: (1.96 + 0.842)² × (0.0475 + 0.051975) / 0.000025 ≈ 31,240.
A new version's TTFT looks 30% worse in the first ten minutes of its canary. Roll back?
Not yet. New replicas start with a cold prefix cache, so early TTFT is pessimistic. Compare after warm-up, against the baseline in the same window, with enough samples. Roll back if it's still worse.
Why is GPU utilisation a poor autoscaling signal for an LLM server?
It measures whether a kernel was running, not whether users are waiting. A continuously batched server can sit at high utilisation across a wide range of load while its queue grows. Queue depth, KV-cache utilisation and pending tokens track user pain directly.
Traffic grows 40 req/s per minute, cold start is 6 minutes and a replica handles 20 req/s. How many replicas of headroom do you need?
Growth during one cold start = 40 × 6 = 240 req/s. 240 / 20 = 12 replicas, either already running, in a warm pool, or added ahead of time by a schedule.
Why scale down slowly?
Removing replicas the moment load dips means paying a full cold start when it rises again minutes later. A cooldown and draining in-flight streams avoid flapping and dropped streams.
Resources
- Google SRE Workbook · Canarying releases: canary design, metric selection and comparison windows.
- Google SRE Book · Handling overload: load shedding and graceful degradation.
- Kubernetes · Horizontal Pod Autoscaler: scaling on custom metrics, stabilisation windows for scale-down.
- KEDA · Concepts: event-driven scaling on queue length and Prometheus metrics.
- vLLM · Metrics: waiting requests, KV-cache usage and latency histograms to scale on.
- SGLang docs and TensorRT-LLM docs: the equivalent metrics and start-up options in the other engines.
- ServerlessLLM: Low-Latency Serverless Inference for Large Language Models (Fu et al., 2024): fast checkpoint loading and locality-aware placement to cut cold starts.
- Efficient Memory Management for LLM Serving with PagedAttention (Kwon et al., 2023): why KV-cache utilisation is the capacity number that matters.