Skip to content
AI.info

Implementation Guides

Deploying LLMs in Production: A Complete Guide

Everything you need to know about serving large language models at scale. Covers model serving frameworks, quantization, batching, scaling, and cost optimization.

Deploying LLMs in Production: A Complete Guide

Gabriele Masetti ·

Choosing a serving framework

Four frameworks cover nearly every production LLM deployment today. Pick based on hardware constraints and traffic shape, not benchmarks alone — the gaps between them are real but usually smaller than the gap between a tuned and untuned deployment of any single one.

Framework Relative throughput Note
vLLM 2-5x vs naive static batching PagedAttention + continuous batching baseline
SGLang ~29% faster than vLLM Heavy prefix-reuse workloads (e.g. Llama-3.1-8B on H100)
TensorRT-LLM 8-13% faster than vLLM On H100/H200, depending on concurrency

Practical rule: default to vLLM. Move to SGLang if your traffic is dominated by shared-prefix requests (support bots, agent frameworks, RAG with a fixed system prompt). Move to TensorRT-LLM only if you have a stable, standardized NVIDIA fleet, latency is the primary KPI, and you have engineering time to maintain compiled engines across model updates.

Quantization

Quantization is usually the highest-leverage lever for both cost and latency, and the choice depends on whether you're GPU-memory-bound or throughput-bound.

Method Effect Detail
AWQ ~2.7-2.9x speedup vs FP16 TinyChat runtime, consumer/edge GPUs
FP8 up to 25-30% higher throughput than FP16 At large batch sizes
FP8 KV cache within ~0.01 perplexity of full precision Minimal quality loss vs full-precision baseline

Default recommendation for a production cluster on Hopper or newer: start with FP8 weights + FP8 KV cache. Drop to AWQ INT4 only if you're memory-constrained and can validate the accuracy trade-off on your own tasks — recall AWQ vs GPTQ vs FP8 all need per-model, per-task validation before you commit; published perplexity numbers on generic benchmarks don't guarantee your task tolerates the same loss.

Batching, KV cache, and speculative decoding

Continuous batching replaces the old model of "wait for a full batch, run it, wait for the next batch" with a scheduler that adds new requests to the running batch as soon as a GPU slot frees up (when another sequence finishes), and evicts finished sequences immediately. This alone is responsible for most of the throughput gain modern servers have over 2022-era batching.

PagedAttention (vLLM) and its equivalents in other engines solve the memory-fragmentation problem continuous batching creates: variable-length sequences waste GPU memory if the KV cache must be contiguous. Paging the cache into fixed-size blocks — and letting multiple sequences share pages when they share a prefix — cuts VRAM usage by more than 50% in long-context, high-prefix-overlap scenarios (chat with a shared system prompt, few-shot prompts, agent scratchpads).

Chunked prefill splits the prompt-processing (prefill) phase into chunks interleaved with decode steps from other requests, instead of letting a long prompt block the GPU for one request at a time. This matters when your workload mixes short and long prompts — without it, a 20K-token prompt request can spike latency for every other concurrent request. Recommended tuning order for a new vLLM deployment: enable continuous batching (on by default), raise gpu_memory_utilization toward 0.90–0.95 to give PagedAttention a bigger pool, then turn on chunked prefill if you see prefill-induced latency spikes.

Speculative decoding is standard in every serious inference engine now (vLLM, SGLang, TensorRT-LLM, LMDeploy). A small draft model proposes several tokens ahead; the full model verifies them in one forward pass, accepting the ones that match what it would have generated anyway. With acceptance rates commonly above 80% on structured or predictable text, this delivers 2–4x speedups in production, and specialized draft methods (EAGLE-3 and similar) push coding-heavy workloads to 3.5–5x.

It's less effective on high-entropy, creative-writing-style output, where the draft model's guesses are rejected more often and the overhead isn't worth it — reserve it for coding assistants, structured extraction, and other constrained-output workloads first.

A minimal vLLM launch reflecting these choices — with the weights left as a variable, because that is the part of the stack that turns over fastest:

MODEL=<your-instruct-checkpoint>     # current open-weight family, see below
DRAFT=<small-checkpoint-same-family> # ~1B, for speculative decoding

vllm serve "$MODEL" \
  --quantization fp8 \
  --kv-cache-dtype fp8 \
  --gpu-memory-utilization 0.92 \
  --enable-chunked-prefill \
  --max-num-seqs 256 \
  --tensor-parallel-size 4 \
  --speculative-config "{\"model\": \"$DRAFT\", \"num_speculative_tokens\": 5}"

The open-weight field turned over twice between 2025 and 2026. A self-hosting leaderboard updated on 20 July 2026 ranks DeepSeek-V4-Pro (1.6T parameters, MIT), Kimi K2.6 (1T, modified MIT) and GLM-5.2 (753B, MIT) at the top, with DeepSeek-V4-Flash (284B, MIT) and Hunyuan Hy3 (295B, Apache 2.0) below them and Llama 3.3 70B several tiers down. Most of the leaders are sparse mixture-of-experts models whose memory footprint bears little relation to the headline parameter count, so size against the specific checkpoint rather than a rule of thumb.

Hardware sizing

GPU choice and count follow directly from model size, quantization, and target concurrency.

GPU VRAM Typical cloud rate (on-demand) Notes
H100 SXM 80 GB on-demand roughly $1.25–$7/hr, median about $3.38 across 40 priced clouds Still the volume workhorse; native FP8
H200 SXM 141 GB from roughly $2.09/hr Same compute as H100, 76% more memory, ~43% more bandwidth — better for long context or larger models per GPU
B200 192 GB on-demand roughly $3.75–$14/hr, median about $6.25 across 36 clouds Blackwell-generation FP8/FP4, and no longer the scarce card it was; Blackwell Ultra (B300, 288 GB) is the tier that now carries the shortage premium, from about $6.50/GPU-hr

Rates vary heavily by provider and commitment level — the same H100 SKU was tracked at $1.25/hr at the cheapest in-stock provider and $6.98/hr at Azure on the day these figures were read. Always get current quotes before budgeting; the table above is a starting point, not a quote.

Sizing a 70B-class dense model in FP8: weights alone are roughly 70 GB. That doesn't fit comfortably on a single 80 GB H100 once you add KV cache and activation memory, so tensor-parallel across 2–4 GPUs is standard — 4x H100 (320 GB pooled) gives comfortable headroom for KV cache at long context and high concurrency; 2x H200 (282 GB pooled) is a leaner alternative given the extra memory per card.

For a 8B-class model, FP8 fits on a single H100/H200 with room for large batches. Always size the KV cache explicitly: cache size per token scales with 2 × num_layers × num_kv_heads × head_dim × dtype_bytes, multiplied by your target concurrent-sequence count and max context length — this is frequently the actual capacity bottleneck, not the weights.

Latency budget

Two metrics dominate user-facing latency SLOs:

These trade off against batch size and concurrency: larger batches raise aggregate throughput but can raise per-request TPOT if the GPU is saturated, and a long prefill from one large request can spike TTFT for everyone else queued behind it (the reason to enable chunked prefill). Load-test at your expected concurrent-request distribution — not a synthetic single-request benchmark — before setting SLOs; vLLM and SGLang both expose per-request TTFT/TPOT histograms so you can see the actual p50/p95/p99 shape under realistic load rather than assuming it from throughput numbers alone.

Scaling and reliability

Don't autoscale LLM inference pods on CPU or raw GPU utilization. CPU is nearly irrelevant to a GPU-bound workload, and GPU utilization is a lagging, misleading signal for engines like vLLM — PagedAttention keeps GPU utilization high by design, so a pod at 85% utilization might be serving 5 requests or 500 with no visible difference in the utilization metric. By the time utilization looks saturated, TTFT p99 has often already blown through its SLO because queueing built up during a burst that the utilization metric smoothed over.

The correct primary signal is queue depth — vLLM exposes it as vllm:num_requests_waiting. Scale out when this exceeds a threshold per replica; scale in when it's near zero. Pair it with GPU-level metrics from DCGM Exporter (dcgm_fi_dev_gpu_util, memory utilization/bandwidth) as a secondary signal, targeting 60–80% sustained compute utilization as healthy headroom rather than a scaling trigger.

KEDA (Kubernetes Event-Driven Autoscaling) is the standard mechanism for this — the default Horizontal Pod Autoscaler reacts too slowly for bursty inference traffic, and KEDA supports scale-to-zero for cost-sensitive or spiky workloads. A minimal KEDA ScaledObject driving off vLLM's own Prometheus metric:

apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: vllm-inference-scaler
spec:
  scaleTargetRef:
    name: vllm-deployment
  minReplicaCount: 1
  maxReplicaCount: 10
  cooldownPeriod: 120
  triggers:
    - type: prometheus
      metadata:
        serverAddress: http://prometheus.monitoring.svc:9090
        metricName: vllm_num_requests_waiting
        query: sum(vllm:num_requests_waiting)
        threshold: "10"

For reliability beyond autoscaling: run health checks that actually exercise inference (a cheap real generation, not just a port check), keep at least one warm replica per model to avoid cold-start latency spikes, and separate GPU pools by model/quantization so a bad deploy on one model doesn't starve capacity from another.

Observability

vLLM and most serving engines expose a Prometheus-compatible /metrics endpoint out of the box. The metrics that matter for an SRE dashboard:

Wire these into Grafana (vLLM ships a reference dashboard) rather than building custom dashboards from scratch, and alert on TTFT/TPOT p99 breaching your SLO threshold, not on GPU utilization alone — by the time utilization alerts fire, users have often already felt the latency.

Cost control: self-host vs. API

The decision between self-hosting and using a hosted API is primarily a volume and utilization question, and it shifts as both API prices and GPU efficiency improve.

Hosted API list pricing per million tokens (input/output), read off each provider's own pricing page on 19 September 2026:

Model Input Output
Claude Fable 5.1 $10.00 $50.00
Claude Opus 5 $5.00 $25.00
Claude Sonnet 5 $2.00 $10.00
Claude Haiku 4.5 $1.00 $5.00
gpt-6-astra $10.00 $50.00
gpt-5.6-terra $2.00 $12.00
gpt-5.6-luna $0.20 $1.20
Gemini 3.1 Pro (preview) $2.00 up to a 200K prompt $12.00
Gemini 3.8 Flash $0.75 through 2026-12-31, then $1.50 $3.75, then $7.50
DeepSeek V4-Pro (cache miss) $0.66–$1.32 $1.98–$3.96

(Several of these carry conditions the table can't hold: Gemini 3.8 Flash's rate is promotional until the end of 2026, Gemini charges more above a 200K-token prompt, and DeepSeek halves its rates off-peak, so the low end of its range is the off-peak price. Cached input is cheaper than the figures above on every one of these platforms. Confirm directly with the provider before a procurement decision.)

Self-hosting a 70B-class model on a small GPU cluster (2–4 H100/H200-class GPUs) typically runs $3,000–5,000/month in all-in infrastructure cost. Whether that beats API pricing depends entirely on sustained volume and utilization — published break-even estimates for a self-hosted 70B model vary widely by source, generally landing somewhere in the range of several billion to over ten billion tokens per month at healthy (70%+) GPU utilization sustained continuously.

Below that volume, or with bursty/low-utilization traffic, hosted APIs are almost always cheaper once you account for the fixed cost of idle GPU capacity, on-call engineering time, and the opportunity cost of not using a frontier model's latest capabilities. The math only clearly favors self-hosting at high sustained volume (tens to hundreds of millions of tokens/day) or when data residency, latency-to-data-locality, or fine-tuning requirements make a hosted API infeasible regardless of cost.

Practical cost-control levers, roughly in order of impact for a self-hosted deployment:

  1. Quantization (FP8 or AWQ) — cuts both memory footprint and cost per token before you touch anything else.
  2. Right-size concurrency and batch limits — an under-utilized GPU is pure waste; tune max-num-seqs and gpu-memory-utilization against your real traffic pattern, not a benchmark default.
  3. Autoscale on queue depth with scale-to-zero for low-traffic environments — don't pay for idle replicas overnight or on weekends if traffic supports it.
  4. Speculative decoding for latency-sensitive, structured-output workloads — improves throughput per GPU without adding hardware.
  5. Route by task difficulty — use a smaller/cheaper model or a lower-cost API tier for simple classification and extraction, and reserve the large self-hosted or frontier-API model for tasks that actually need it.

Re-evaluate the self-host/API decision periodically rather than once — both hosted API pricing and open-weight model quality shift fast enough that a decision made a year ago is worth re-checking against current numbers.

Explore

More articles