Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Decision SOP for serving LLMs with vLLM. Covers PagedAttention mental model, quantization/parallelism/batching tradeoffs, OOM triage, and when NOT to use vLLM. Activates when a coder-agent is choosing or tuning an inference engine, debugging vLLM throughput/latency/OOM, or comparing vLLM against TGI/SGLang/TensorRT-LLM/llama.cpp.
.claude/skills/agentsope-agentsop-vllm/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-05 | ✗→✓ | ▲ Improved | 301% | 0% |
| case-11 | ✗→✓ | ▲ Improved | 663% | 0% |
| case-18 | ✗→✓ | ▲ Improved | 158% | 0% |
| case-26 | ✗→✓ | ▲ Improved | 404% | 0% |
| case-01 | ✓→✓ | = Same ✓ | 183% | 0% |
Activate this skill when any of the following hold:
Do NOT activate for: training/fine-tuning (use accelerate/deepspeed/trl), CPU-only edge inference (use llama.cpp/Ollama), Apple Silicon production (vLLM Metal/MPS is experimental, not production-ready as of 2026) aimadetools.com 2026], API-only consumption of hosted models (just call the OpenAI/Anthropic SDK).
vLLM's defining insight (Kwon et al., SOSP 2023) is that LLM serving's bottleneck was not compute — it was KV-cache memory fragmentation. Pre-vLLM systems pre-allocated a contiguous KV-cache slot per request, sized for the maximum possible output length; in early 2023, inference engines used only 20–40% of available GPU memory because of internal+external fragmentation arxiv.org/abs/2309.06180; zilliz.com/learn].
PagedAttention applies classic OS paging to KV cache:
Result: near-zero memory waste → larger batch sizes → 2–4× throughput vs FasterTransformer/Orca at equal latency arxiv.org/abs/2309.06180]; 14–24× vs vanilla HuggingFace Transformers yottalabs.ai 2026].
vLLM inherits Orca's iteration-level scheduling (OSDI 2022, 36.9× over FasterTransformer medium.com/byte-sized-ai]). Instead of waiting for a static batch to finish, the scheduler reassigns batch slots every decode step: a request that finishes early frees its slot to a waiting request. Static batching is dead; continuous batching is table stakes.
Takeaway: when tuning, separate TTFT (time-to-first-token, gated by prefill+queue) from ITL (gated by decode bandwidth and batch interference).
Per Red Hat's tuning hierarchy developers.redhat.com 2026]:
[Step 0] Confirm vLLM is the right tool
├─ Production, GPU-backed, concurrent users? → continue
└─ Else → see §7 (ecosystem) and stop
[Step 1] Pick the model + precision
├─ Model fits in single-GPU VRAM at BF16? → keep BF16, TP=1
├─ Need 50% VRAM cut, ~zero quality loss? → FP8 (Hopper/Ada+) [arxiv 2411.02355]
├─ Need 4× VRAM cut, tolerate ~1.6pt avg drop? → AWQ-4 or GPTQ-4
└─ Reasoning-heavy / coding workload? → favor FP8 > AWQ; verify on eval set
[Step 2] Choose parallelism
├─ Fits 1 GPU → TP=1, PP=1
├─ Fits 1 node, NVLink present → TP=#GPUs/node
├─ Fits 1 node, only PCIe (e.g. L40S) → PP within node (TP-only over PCIe collapses)
├─ Multi-node → TP=GPUs/node, PP=#nodes
└─ MoE model (Mixtral, DSv3) → DP attention + EP/TP for MoE layers
[Step 3] Set memory/batch envelope
├─ --gpu-memory-utilization 0.90 (default; 0.85 if sharing GPU)
├─ --max-model-len = (longest realistic prompt + output) — NOT model max!
├─ --max-num-seqs (start 256; lower if preemption logs appear)
└─ --max-num-batched-tokens (raise for TTFT; lower for ITL)
[Step 4] Turn on the free wins
├─ enable_prefix_caching=True → if any system-prompt/few-shot reuse
├─ enable_chunked_prefill=True (V1: default on) → tame long-prompt HoL blocking
└─ kv_cache_dtype="fp8" → +KV headroom, Ampere+ only
[Step 5] Optional: speculative decoding
├─ Low QPS, latency-bound, have draft/EAGLE weights? → EAGLE-3 or MTP (high gain)
├─ No draft model, zero setup cost? → n-gram (modest gain, ~1.17×)
└─ High QPS / large batch? → skip; gains shrink, complexity rises
[Step 6] Benchmark on YOUR workload
├─ Replay representative ISL/OSL distribution
├─ Watch Prometheus: num_requests_waiting, KV cache occupancy, preemption count
└─ Tune in this order: §3 Step 3 → Step 4 → Step 5 → reconsider Step 2
[Step 7] Scale out
├─ Latency SLA violated under load? → add a replica (data parallelism across pods)
├─ Tail TTFT high? → prefix-aware routing; pin prefix to replica
└─ Cost too high? → revisit quantization, smaller model, draft modeltorch.OutOfMemoryError: CUDA out of memory during engine init or warmup.--max-model-len to the realistic max (prompt + output), not the model's architectural max markaicode.com 2026].--gpu-memory-utilization to 0.85 if other processes share the GPU; raise to 0.95 if vLLM is alone and KV cache is too small.--kv-cache-dtype fp8 (Ampere+) or --enforce-eager (skip CUDA-graph reservation).--max-num-seqs (e.g. 256 → 64 → 16).--tensor-parallel-size to shard the model.--quantization {fp8|awq|gptq|...} flag; documented expected accuracy delta.--tensor-parallel-size=<GPUs in node>, PP=1.--pipeline-parallel-size instead of large TP — all-reduce over PCIe will tank TP throughput docs.vllm.ai parallelism_scaling].TP=GPUs/node, PP=#nodes. Use InfiniBand if possible.--enable-prefix-caching (in V1, often on by default).num_requests_waiting > 0 sustained → engine queue-bound; check num_requests_running.--max-num-seqs or quantize KV to FP8.--speculative-config JSON, with measured tokens/s delta on representative traffic.Situation: A team serves Llama-3-70B-FP8 on 2×H100. P50 TTFT is fine at 250 ms, but P99 spikes to 4 s when traffic bursts. They consider raising --max-num-seqs from 64 to 256 to handle bursts.
Tension:
max_num_seqs → more concurrency → higher throughput, but also more contention for KV cache → preemption + head-of-line blocking on prefills.max_num_batched_tokens → better TTFT (more prefill per step) → worse ITL for streaming requests already mid-decode.Resolution heuristic:
num_requests_waiting > 0 and KV occupancy < 80%: raise max_num_seqs.max_model_len.max_num_batched_tokens (e.g. 2048) to favor decode/ITL; for batch/offline: raise (≥8192) to favor TTFT/throughput docs.vllm.ai optimization; anyscale.com].Evidence: developers.redhat.com 2026 5-steps-triage]; medium.com/@kaige.yang0110].
Situation: Deploying Qwen-72B on a single H100 (80 GB). At BF16, model is 144 GB → doesn't fit. Options: (a) FP8, 72 GB → fits with tight KV budget; (b) AWQ-4, 36 GB → comfortable KV budget, larger batches.
Tension:
Resolution heuristic:
Evidence: arxiv.org/abs/2411.02355]; docs.gpustack.ai vLLM quantization].
Situation: 4×A100-80GB available, NVLink. Choice: one big replica (TP=4, max single-request latency optimized) or two replicas (TP=2, more concurrency).
Tension:
Resolution heuristic:
vllm bench serve on representative ISL/OSL.Evidence: docs.vllm.ai parallelism_scaling]; docs.jarvislabs.ai scaling-llm-inference-dp-pp-tp].
Situation: Production RAG service. Each request has a 2 KB system prompt + 4–16 KB context + short user query. They wonder whether enable_prefix_caching helps enough to justify the held KV blocks.
Tension:
Resolution heuristic:
Evidence: docs.vllm.ai/en/stable/design/prefix_caching/]; bentoml.com/llm/inference-optimization/prefix-caching].
Situation: Single-user, latency-sensitive coding assistant on Llama-3-70B. Decode dominates total latency.
Tension:
Resolution heuristic:
Evidence: docs.vllm.ai/en/latest/features/speculative_decoding/]; developers.redhat.com 2025 fly-eagle3-fly].
max_model_len to the model's architectural max "just in case"A Llama-3.1 model supports 128k context. If you serve 4k-prompt workloads but set --max-model-len 131072, vLLM reserves KV-cache slots for the worst case → smaller batches → throughput collapse. Set it to your realistic max (longest_prompt + longest_output + safety margin) markaicode.com 2026].
APC accelerates prefill only. If outputs are long and prefixes don't repeat, gain ≈ 0 — and you've consumed KV memory for nothing docs.vllm.ai automatic_prefix_caching]. Measure shared-prefix fraction before enabling for high-pressure workloads.
Tensor parallelism issues an all-reduce after each layer. NVLink (≈900 GB/s bidirectional) handles it; PCIe Gen4 x16 (≈32 GB/s, ~28× slower) does not spheron.network 2026]. On L40S / consumer cards / cross-NUMA setups, use pipeline parallelism or independent replicas instead docs.vllm.ai parallelism_scaling].
Academic benchmarks (MMLU, HellaSwag) show GPTQ ≈ AWQ; real coding workloads can show meaningfully larger gaps between methods, and quality at INT3 collapses (~6-point drop) arxiv.org/abs/2411.02355]. Always test on your eval set.
vLLM needs minimum 2 + N physical CPU cores for N GPUs; under-provisioning CPUs makes the API server, tokenizer, and detokenizer the bottleneck before the GPU is touched docs.vllm.ai/en/stable/configuration/optimization/]. Scale CPU and use --api-server-count when needed.
--enforce-eager in production "for stability"--enforce-eager skips CUDA graph capture → slower decode. It's a debugging/memory-OOM workaround, not a steady-state production setting. Fix the underlying OOM (KV dtype, max_model_len, max_num_seqs) and re-enable CUDA graphs.
vLLM is the right tool when:
vLLM is the wrong tool when:
guided_decoding (consider SGLang for complex agent state machines).| Engine | Sweet spot | When to pick over vLLM | |---|---|---| | vLLM | Production GPU serving, mixed traffic, open weights, vendor-neutral | Default first choice for throughput-oriented LLM serving in 2026 | | TGI (HuggingFace) | Was the HF default | Officially in maintenance mode; HF themselves now recommend vLLM or SGLang yottalabs.ai 2026] | | SGLang | Heavy prefix sharing, agent/RAG state machines, structured generation | ~29% higher throughput than vLLM when requests share context (chatbots, RAG, agents) thanks to RadixAttention prefix tree n1n.ai 2026] | | TensorRT-LLM | Single-vendor NVIDIA, max throughput, willing to invest setup | Up to 30–50% higher throughput than vLLM in high-concurrency NVIDIA-only deployments; 1–2 weeks setup; vendor lock-in n1n.ai 2026] | | llama.cpp | CPU, edge, Apple Silicon, single-user, GGUF | No GPU available; ≤1 concurrent user; minimal-deps deploy aimadetools.com 2026] | | Ollama | Local dev, prototyping, model switching | Developer ergonomics over throughput; 5-minute setup contracollective.com 2026] |
Common pattern: develop on Ollama → benchmark with vLLM → consider SGLang if prefix-sharing workload → consider TensorRT-LLM only if NVIDIA-locked and engineering budget is large.
A production-ish vLLM serve for Llama-3.1-70B-Instruct-FP8 on 2×H100 with NVLink, RAG-style workload:
bashvllm serve meta-llama/Llama-3.1-70B-Instruct \ --quantization fp8 \ --tensor-parallel-size 2 \ --max-model-len 8192 \ --max-num-seqs 256 \ --max-num-batched-tokens 8192 \ --gpu-memory-utilization 0.90 \ --enable-prefix-caching \ --enable-chunked-prefill \ --kv-cache-dtype fp8
Then triage with the §4 OP-5 5-step workflow on real traffic before tuning further.
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | pass→pass | 21,052 | 19,705 | -6% | 1 | 1 | 0% | 3,798 | 10,761 | +183% | 0 | 0 | — |
case-02 | fail→fail | 14,891 | 9,702 | -35% | 1 | 1 | 0% | 2,325 | 8,840 | +280% | 0 | 0 | — |
case-03 | pass→pass | 16,175 | 13,240 | -18% | 1 | 1 | 0% | 2,902 | 9,728 | +235% | 0 | 0 | — |
case-04 | pass→pass | 11,293 | 8,614 | -24% | 1 | 1 | 0% | 1,765 | 8,696 | +393% | 0 | 0 | — |
case-05 | fail→pass | 14,076 | 16,599 | +18% | 1 | 1 | 0% | 2,482 | 9,951 | +301% | 0 | 0 | — |
case-06 | pass→pass | 16,624 | 17,210 | +4% | 1 | 1 | 0% | 2,809 | 10,242 | +265% | 0 | 0 | — |
case-07 | pass→pass | 21,436 | 7,584 | -65% | 1 | 1 | 0% | 1,698 | 8,410 | +395% | 0 | 0 | — |
case-08 | pass→pass | 18,229 | 14,736 | -19% | 1 | 1 | 0% | 2,845 | 9,455 | +232% | 0 | 0 | — |
case-09 | pass→pass | 16,283 | 10,821 | -34% | 1 | 1 | 0% | 2,851 | 9,049 | +217% | 0 | 0 | — |
case-10 | pass→pass | 16,205 | 11,863 | -27% | 1 | 1 | 0% | 2,747 | 9,109 | +232% | 0 | 0 | — |
case-11 | fail→pass | 19,777 | 8,755 | -56% | 1 | 1 | 0% | 1,137 | 8,671 | +663% | 0 | 0 | — |
case-12 | pass→pass | 6,328 | 8,576 | +36% | 1 | 1 | 0% | 1,057 | 8,686 | +722% | 0 | 0 | — |
case-13 | pass→pass | 15,820 | 10,186 | -36% | 1 | 1 | 0% | 2,405 | 8,743 | +264% | 0 | 0 | — |
case-14 | pass→pass | 16,658 | 17,552 | +5% | 1 | 1 | 0% | 2,675 | 9,942 | +272% | 0 | 0 | — |
case-15 | pass→pass | 16,612 | 9,983 | -40% | 1 | 1 | 0% | 2,458 | 8,818 | +259% | 0 | 0 | — |
case-16 | pass→pass | 14,601 | 7,327 | -50% | 1 | 1 | 0% | 2,394 | 8,403 | +251% | 0 | 0 | — |
case-17 | pass→pass | 14,662 | 12,524 | -15% | 1 | 1 | 0% | 2,230 | 9,201 | +313% | 0 | 0 | — |
case-18 | fail→pass | 21,911 | 13,673 | -38% | 1 | 1 | 0% | 3,675 | 9,499 | +158% | 0 | 0 | — |
case-19 | pass→pass | 13,635 | 10,299 | -24% | 1 | 1 | 0% | 2,107 | 8,879 | +321% | 0 | 0 | — |
case-20 | pass→pass | 5,649 | 5,148 | -9% | 1 | 1 | 0% | 901 | 8,018 | +790% | 0 | 0 | — |
case-21 | pass→pass | 4,347 | 5,483 | +26% | 1 | 1 | 0% | 654 | 8,051 | +1131% | 0 | 0 | — |
case-22 | pass→pass | 5,917 | 5,022 | -15% | 1 | 1 | 0% | 961 | 7,975 | +730% | 0 | 0 | — |
case-23 | pass→pass | 17,583 | 16,052 | -9% | 1 | 1 | 0% | 2,965 | 9,830 | +232% | 0 | 0 | — |
case-24 | pass→pass | 10,006 | 5,410 | -46% | 1 | 1 | 0% | 1,470 | 8,084 | +450% | 0 | 0 | — |
case-25 | pass→pass | 8,624 | 6,862 | -20% | 1 | 1 | 0% | 1,382 | 8,347 | +504% | 0 | 0 | — |
case-26 | fail→pass | 11,785 | 12,899 | +9% | 1 | 1 | 0% | 1,842 | 9,280 | +404% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 26 cases were attempted, and 25 counted toward the lift figure. The other 1 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +15 percentage points is the difference between those two pass rates over the 25 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.