Production and economics
Interactive and batch pools
Run chat on reserved replicas with TTFT SLOs. Put offline jobs on spare or spot capacity with preemption so they cannot steal the interactive KV.
A serving platform that mixes interactive chat and offline summarization on one pool will miss the chat SLO the first time a batch job grabs KV. Split the work. Interactive replicas hold a reserved floor. Batch consumes spare capacity, or a cheaper queue, and can be killed.
A typical mix: ~50% interactive chat (1–4k input, 200–800 output, strict SLO), ~30% RAG/agent (4–32k input, cacheable prefixes), ~20% batch/offline (mixed, soft latency, preemptible).
Concept
Preemption means the scheduler or gateway cancels a lower-priority request to free KV and batch slots. The batch job retries later. Interactive tokens already in flight keep their seats.
Walking through the platform pieces
Interactive pool. 70B-class FP8, modest tensor parallel (TP=2 on a node is common), continuous batching, prefix caching, multi-LoRA on a shared base. Cap concurrent LoRAs in GPU RAM. Spill cold adapters to CPU or NVMe. Route by tenant_id → adapter_id at the gateway. See multi-LoRA.
Gateway. Auth, per-tenant token quotas and $ budgets, model and LoRA routing, prefix-aware routing, priority lanes (interactive > batch). Retries with idempotency keys where safe. Never blindly duplicate side-effecting tool calls. See routing.
Autoscaling. Scale on waiting queue, KV utilization and TTFT, not GPU util. minReplicas sized for cold-start × ramp (weights are 100 GB-class). Warm pool of ready replicas for evening peaks. Separate HPA for batch workers. See autoscaling.
Batch tier. Run offline jobs on spare interactive capacity with preemption (kill batch mid-request when KV pressure rises), or on spot / older GPUs with a queue (Kafka/SQS) and no TTFT SLO. This is how you raise utilization toward the optimistic $/M without missing chat SLOs.
Trade-off
Colocated vs disaggregated prefill/decode. Colocated is simpler ops and enough for many chat mixes. Disaggregate when RAG prefill steals decode capacity and TTFT/TPOT fight each other on the same GPU — at the cost of KV transfer and two pools to autoscale. Start colocated. Split when benchmarks show prefill interference. See disaggregated serving.
70B in FP16 needs ~140 GB weights alone. FP8 roughly halves weight memory, frees KV headroom, and is the default starting point for 70B-class interactive serving as of when this was written. Validate with evals. Keep a BF16 canary path for quality cliffs.
Pipeline parallel is usually wrong for tight TPOT SLOs unless model size forces it. Expert parallel only if you actually serve an MoE.
The big idea
A hospital keeps ICU beds reserved and puts elective surgery on a list that can slip. Chat is the ICU. Batch is elective. The picture breaks if "reserved" still means one shared KV arena with no preemption: then the elective case still evicts the emergency.
Common mistakes
- One HPA on GPU util for chat and batch. GPU looks busy while TTFT burns.
- No preemption. A 100k-token offline job pins the interactive replica.
- Floating
latesttags for LoRAs, so a tenant silently swaps weights.
Check yourself
- What three signals should scale the interactive pool? Answer: waiting queue, KV utilization and TTFT — not GPU util.
- How does batch raise utilization without stealing chat? Answer: run it on spare or spot and preempt it when KV pressure rises.
Key takeaways
- Split interactive and batch. Preempt batch first.
- Scale interactive on queue, KV and TTFT. Keep a warm pool for cold starts.
- FP8 plus modest TP plus prefix cache is the usual 70B interactive stack.
- Start colocated. Disaggregate only when prefill fights decode.
Further learning
Next, drill node loss, spikes and bad rollouts.
Related
Case study: designing an LLM serving platform
A 70B-class model and LoRA fleet at 50k RPM under TTFT and TPOT SLOs. Capacity math, pools, gateway and failure drills.
Autoscaling and GPU fleet management
Scale GPU replicas on queue depth, KV pressure and SLOs — not GPU util — then plan fleets around cold starts, warm pools and heterogeneous hardware.
Capacity from goodput
Convert peak RPM to tokens per second, then divide by SLO-constrained goodput per replica. Peak blog tok/s is the wrong denominator.