LLM Inference Servers in Python: vLLM vs TGI vs SGLang Compared (2026)

Compare vLLM, TGI, and SGLang for serving LLMs on your own GPUs in 2026. Throughput numbers, prefix caching, quantization tradeoffs, and a production Docker deployment with monitoring.

vLLM vs TGI vs SGLang: 2026 Guide

Updated: June 13, 2026

LLM inference servers like vLLM, TGI, and SGLang are specialized runtimes that maximize GPU throughput when serving large language models in production. vLLM is the throughput champion thanks to PagedAttention and the largest model coverage. TGI is the safest pick if you already live inside the Hugging Face stack. SGLang is the latency winner for structured workloads, because RadixAttention turns shared prompt prefixes into near-free KV cache hits. After running all three on a real product, here's how I pick between them in 2026.

  • vLLM 0.9 delivers the highest sustained tokens/sec on most open-weight models and supports the widest hardware (NVIDIA, AMD ROCm, AWS Neuron, TPU).
  • TGI 3.x has the smoothest Hugging Face integration, the cleanest Docker images, and best-in-class observability via OpenTelemetry out of the box.
  • SGLang 0.4 wins on TTFT (time-to-first-token) and structured-output workloads thanks to RadixAttention prefix caching and a compiled frontend.
  • All three speak the OpenAI /v1/chat/completions API, so the client code does not change when you swap engines.
  • Continuous batching is now table stakes; the real differentiators in 2026 are prefix caching, speculative decoding, FP8/INT4 support, and disaggregated prefill.
  • Choose based on workload shape: long shared prompts go to SGLang, mixed unbounded traffic goes to vLLM, HF-centric teams go to TGI.

What is an LLM inference server?

An LLM inference server is a long-running process that loads model weights into GPU memory once and then serves many concurrent generation requests against them, applying throughput optimizations that a naive model.generate() loop cannot. The naive loop wastes 70–95% of GPU compute because requests finish at different lengths, the KV cache fragments memory, and there's no batching across requests. An inference server fixes all of that. It pages the KV cache, batches tokens at every decode step (continuous batching), reuses identical prompt prefixes across users, and exposes an HTTP API your application can call.

So, in 2026 the three engines that matter for open-weight model serving on your own GPUs are vLLM, Hugging Face TGI, and SGLang. They all support Llama 3.x and 4, Qwen 3, DeepSeek V3/R1, Mistral, Phi-4, and the major multimodal models (Llava-Next, Qwen-VL, Pixtral). They all expose the OpenAI Chat Completions API. The differences sit one layer down: in the attention kernel, the scheduler, the cache hierarchy, and the operational surface area.

If you're still serving LLMs with FastAPI wrapping transformers.pipeline(), you're leaving 10–40x throughput on the floor. That works for a single-user notebook. It does not work for an on-call rotation. I learned this the hard way reviewing a six-figure monthly GPU bill that should have been a four-figure one, and the fix was a one-line engine swap. (For non-LLM model serving, see our companion piece on BentoML, Ray Serve, FastAPI, and Triton compared.)

vLLM vs TGI vs SGLang at a glance

Here's the side-by-side I use when sketching architecture for a new LLM serving deployment. Numbers are from internal benchmarks on an 8×H100 node serving Llama-3.3-70B-Instruct at FP8, with realistic chat traffic (4096 input tokens, 256 output tokens, Poisson arrivals at 0.8 saturation). Your mileage will vary, but the relative ordering has been stable for the last two release cycles.

FeaturevLLM 0.9TGI 3.xSGLang 0.4
Throughput (tokens/sec, 70B FP8)~4,800~3,900~4,600
P50 time-to-first-token (4k context)180ms210ms120ms
Prefix cache hit speedup2–3x1.5–2x3–5x (RadixAttention)
Hardware supportNVIDIA, AMD, TPU, Neuron, IntelNVIDIA, AMD, GaudiNVIDIA, AMD
Quantization formatsFP8, AWQ, GPTQ, INT4, BitsAndBytesFP8, AWQ, GPTQ, EETQFP8, AWQ, GPTQ, INT4
Speculative decodingYes (EAGLE, MEDUSA, ngram)Yes (Medusa, ngram)Yes (EAGLE-2, EAGLE-3)
Structured output (JSON schema)Outlines, xgrammarOutlinesNative (xgrammar, compressed FSM)
OpenAI API compatibilityFullFullFull
Operational learning curveMediumLowMedium-high
LicenseApache 2.0Apache 2.0 (HFOIL on commercial managed)Apache 2.0

The headline: vLLM is the median-best default, TGI is the smoothest if you're already deep in the Hugging Face ecosystem, and SGLang is what you reach for when latency or shared-prefix workloads dominate. None of these will ever be the right answer for a closed-API service like Claude or GPT-4o, since those have their own hosted inference and you don't need a serving framework, just an SDK call. This article assumes you are running open-weight models on GPUs you pay for.

How vLLM works (PagedAttention and continuous batching)

vLLM's core innovation is PagedAttention, which treats the KV cache the way an operating system treats virtual memory: fixed-size blocks instead of one contiguous tensor per request. The practical effect is that you can pack many more concurrent sequences into the same GPU memory because there's no internal fragmentation, and prefix sharing becomes a pointer swap instead of a memory copy. Combined with continuous batching (the scheduler revisits the batch composition every decode step instead of every request), vLLM keeps the tensor cores busy even when individual requests have wildly different lengths.

Starting a vLLM server is one command:

# Install on a CUDA 12.4+ host with at least 80GB VRAM
pip install "vllm==0.9.*"

# Serve Llama-3.3-70B-Instruct quantized to FP8 across 4 GPUs
vllm serve meta-llama/Llama-3.3-70B-Instruct \
    --tensor-parallel-size 4 \
    --quantization fp8 \
    --max-model-len 32768 \
    --enable-prefix-caching \
    --enable-chunked-prefill \
    --gpu-memory-utilization 0.90 \
    --port 8000

And call it like any OpenAI endpoint:

from openai import OpenAI

client = OpenAI(base_url="http://localhost:8000/v1", api_key="EMPTY")

resp = client.chat.completions.create(
    model="meta-llama/Llama-3.3-70B-Instruct",
    messages=[{"role": "user", "content": "Summarize the CAP theorem in 2 sentences."}],
    max_tokens=128,
    temperature=0.2,
)
print(resp.choices[0].message.content)

The flags that matter in production: --enable-prefix-caching is free throughput if your prompts share a system message. --enable-chunked-prefill stops a long-prompt request from starving everyone else's decode steps. --gpu-memory-utilization 0.90 is the highest I push it on H100s without OOMing on KV cache spikes. Read the official vLLM documentation for the full flag matrix, since it changes every minor release.

How TGI works (HF integration and observability)

Hugging Face's Text Generation Inference is the Rust-based serving runtime that the HF Inference Endpoints product runs on. The Rust frontend handles request routing, batching, and the HTTP layer. The Python backend handles model execution with Flash Attention 2/3. The pitch is operational polish: prebuilt container images for every major model family, OpenTelemetry traces out of the box, structured JSON logs, a Prometheus /metrics endpoint that exposes batch fill rate and KV cache utilization without configuration.

The launch command is similarly clean:

docker run --gpus all --shm-size 1g -p 8080:80 \
    -v $PWD/models:/data \
    ghcr.io/huggingface/text-generation-inference:3.1 \
    --model-id meta-llama/Llama-3.3-70B-Instruct \
    --num-shard 4 \
    --quantize fp8 \
    --max-input-tokens 32000 \
    --max-total-tokens 32256 \
    --otlp-endpoint http://collector:4317

What I like about TGI in practice: the launcher validates your model is compatible before it even tries to load weights. The Docker images pin every CUDA and PyTorch version and have been working for me on AWS, GCP, and bare-metal H100s without a single dependency-hell incident in 18 months. The downside? TGI's throughput trails vLLM by 15–25% on dense models in my benchmarks, and the project's commercial license clause (HFOIL for managed offerings) means some legal teams flag it even though self-hosting is straightforward Apache 2.0. See the official TGI documentation for the current compatibility matrix.

How SGLang works (RadixAttention and structured outputs)

SGLang takes a different bet. Instead of treating each request as an independent string, it builds a radix tree of all KV caches in the cluster and reuses any matching prefix automatically. They call this RadixAttention, and on workloads with shared system prompts, few-shot exemplars, or multi-turn conversations it is genuinely transformative. On a customer support chatbot I benchmarked last quarter, SGLang's cache hit rate hit 78% in production and TTFT dropped from 240ms (vLLM with prefix caching on) to 95ms.

The second SGLang win is structured outputs. The team built xgrammar and a compressed FSM directly into the decoding loop, so JSON schema constraints add almost no latency overhead. Compare that with Outlines bolted onto vLLM, which I have measured at 15–30% throughput tax on complex schemas.

# Install and serve
pip install "sglang[all]==0.4.*"

python -m sglang.launch_server \
    --model-path meta-llama/Llama-3.3-70B-Instruct \
    --tp 4 \
    --quantization fp8 \
    --enable-radix-cache \
    --port 30000

Structured generation feels almost too easy:

from openai import OpenAI

client = OpenAI(base_url="http://localhost:30000/v1", api_key="EMPTY")

schema = {
    "type": "object",
    "properties": {
        "sentiment": {"type": "string", "enum": ["positive", "negative", "neutral"]},
        "confidence": {"type": "number", "minimum": 0, "maximum": 1},
    },
    "required": ["sentiment", "confidence"],
}

resp = client.chat.completions.create(
    model="meta-llama/Llama-3.3-70B-Instruct",
    messages=[{"role": "user", "content": "Classify: 'shipping was fast but the package was damaged'"}],
    response_format={"type": "json_schema", "json_schema": {"name": "sentiment", "schema": schema}},
    max_tokens=128,
)
print(resp.choices[0].message.content)

The cost of SGLang's power? More operational surface area. The radix cache eviction policy is tunable (--mem-fraction-static, --schedule-policy) and you will tune it. SGLang's CUDA graph capture is more memory-hungry than vLLM's, and it leaves less headroom for KV spikes. I have OOMed nodes that ran fine on vLLM with identical traffic.

Which LLM inference server is fastest in 2026?

There's no single answer, because "fastest" depends on what you're measuring. For sustained throughput (tokens/sec across the full batch) under unbounded mixed traffic, vLLM 0.9 wins on most dense open-weight models. Its scheduler and CUDA graph fast paths are the most polished. For TTFT on workloads where requests share prompt prefixes, SGLang wins by a wide margin because RadixAttention turns prefill into a cache lookup. For P99 latency under bursty traffic, TGI is the most predictable because its scheduler is the most conservative; it will refuse new requests sooner instead of letting queues balloon.

What killed my naive "vLLM is always fastest" assumption: when DeepSeek-V3 (the MoE model) landed, SGLang shipped expert-parallel support a full release cycle ahead of vLLM, and was 1.6x faster on it for two months. Then vLLM caught up. Engine performance leapfrogs every 4–8 weeks. Pick based on workload shape and architectural fit, not last quarter's benchmark, and re-benchmark before every major release.

Quantization support: FP8, AWQ, GPTQ, INT4

All three engines support the major quantization formats in 2026, but with different defaults and different tradeoffs.

  • FP8 (E4M3 or E5M2): the new default on Hopper (H100) and Blackwell (B100/B200) GPUs. Roughly 2x throughput vs FP16 with <1% quality loss on most benchmarks. All three engines support it via the --quantization fp8 flag, and vLLM additionally supports KV-cache FP8 quantization for ~30% more concurrent sequences.
  • AWQ (Activation-aware Weight Quantization): 4-bit weights, FP16 activations. Best quality-per-bit at 4-bit. Lossless on most chat benchmarks. Required when serving a 70B model on a single 80GB GPU.
  • GPTQ: the older 4-bit quantization standard. Slightly worse than AWQ on quality but with broader checkpoint availability on Hugging Face. Use if your team already has GPTQ artifacts; otherwise prefer AWQ.
  • INT4 (Marlin/Machete kernels): the fastest 4-bit inference path on Ampere and Hopper. vLLM and SGLang auto-select Marlin for AWQ/GPTQ checkpoints; TGI uses EETQ.

Cost-per-prediction matters here. On an A100-80GB at $1.10/hr (3-year reserved), serving Llama-3.3-70B at FP16 costs about $0.0042 per 1k output tokens. The same model at FP8 on an H100 costs $0.0019 per 1k output tokens, under half. Quantization is the single biggest cost lever you have, and all three engines make it a CLI flag.

How to deploy vLLM in production with Docker

For a real on-call rotation, you want a containerized deployment with health checks, graceful shutdown, structured logs, and metrics. Here's the production docker-compose.yml I use as a starting point:

version: "3.8"
services:
  vllm:
    image: vllm/vllm-openai:v0.9.0
    runtime: nvidia
    environment:
      - HF_TOKEN=${HF_TOKEN}
      - VLLM_LOGGING_LEVEL=INFO
      - VLLM_USAGE_SOURCE=production
      - NCCL_DEBUG=WARN
    volumes:
      - ./hf_cache:/root/.cache/huggingface
    ports:
      - "8000:8000"
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 4
              capabilities: [gpu]
    command: >
      --model meta-llama/Llama-3.3-70B-Instruct
      --tensor-parallel-size 4
      --quantization fp8
      --max-model-len 32768
      --enable-prefix-caching
      --enable-chunked-prefill
      --gpu-memory-utilization 0.90
      --disable-log-requests
      --max-num-seqs 256
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 600s

A few hard-won notes. The start_period: 600s is not paranoid. Llama-3.3-70B takes 4–7 minutes to load weights from the HF cache on cold start, and Kubernetes will kill the pod before it is ready if you set this too low. --disable-log-requests is essential at any non-trivial traffic level; the per-request logs will swamp your stdout pipeline. --max-num-seqs 256 caps concurrency. Leaving it unbounded lets a traffic spike crater your TTFT for everyone in the batch.

For autoscaling, don't autoscale on CPU or memory. Scale on vllm:num_requests_running from the Prometheus metrics endpoint or on token-rate. CPU is always 100% (it is serializing the GPU outputs) and memory is always nearly 100% (you intentionally filled the KV cache).

Monitoring LLM serving in production

The four golden signals for LLM serving are not the standard four. You want:

  1. TTFT (time to first token): the latency users actually perceive. Alert if P95 > 1s sustained.
  2. TPOT (time per output token): the streaming speed. Alert if P95 > 60ms (slower than reading speed).
  3. KV cache utilization: >90% sustained means you are about to start preempting. Scale out.
  4. Batch fill rate: <30% sustained means you are paying for idle GPU. Scale in.

All three engines expose these as Prometheus metrics. vLLM uses the vllm: prefix, TGI uses tgi_, SGLang uses sglang_. Wire them into Grafana and put TTFT and TPOT on the on-call dashboard before you wire up any other dashboard. Closing the feedback loop on drift and quality is a separate problem. See our guide on data drift detection in Python for the model-output side, and on ML experiment tracking for tying production metrics back to training runs.

How to choose: a decision tree

Honestly, this is the decision tree I draw on whiteboards:

  • If your workload has long shared prompts (RAG with cached context, multi-turn chat, few-shot exemplars): start with SGLang. RadixAttention is a step-function improvement, not a marginal one.
  • If your team lives in the Hugging Face ecosystem, your platform team values operational polish over peak throughput, and you need OpenTelemetry traces yesterday: TGI. Lowest operator cognitive load of the three.
  • If you have unbounded mixed traffic, exotic hardware (AMD MI300, AWS Neuron, TPUs), or you need a specific model the day it drops on Hugging Face: vLLM. The community is the largest and the model coverage is the widest.
  • If you have no idea: start with vLLM. It's the safest default, the documentation is the most complete, and you can swap to SGLang or TGI later because the API is identical.

One more thing I've learned the expensive way: don't run two engines side-by-side "to compare in production" without a feature flag and a kill switch. I've seen multi-day incidents caused by a "we'll just A/B the engines" rollout that drifted into "the SGLang nodes are running a different model version because someone forgot to bump the tag." Pick one, ship it, instrument it, revisit it next quarter.

Frequently Asked Questions

Is vLLM better than Hugging Face TGI?

vLLM typically delivers 15–25% higher throughput on dense models in 2026 benchmarks, but TGI has smoother Hugging Face integration, prebuilt OpenTelemetry tracing, and a more conservative scheduler that produces more predictable P99 latency. Choose vLLM for raw throughput, TGI for operational polish.

What is SGLang used for?

SGLang is an LLM serving engine optimized for workloads with shared prompt prefixes (RAG, multi-turn chat, few-shot prompting) and for structured outputs (JSON schema, regex constraints). Its RadixAttention prefix cache and compiled grammar decoding make it the fastest engine for these workloads.

Does vLLM support quantization?

Yes. vLLM 0.9 supports FP8, AWQ, GPTQ, INT4 (via Marlin/Machete kernels), and BitsAndBytes. FP8 on Hopper or Blackwell GPUs is the recommended default, about 2x throughput vs FP16 with under 1% quality regression on most benchmarks.

Can I use vLLM, TGI, and SGLang with the OpenAI Python SDK?

Yes. All three engines expose the OpenAI /v1/chat/completions and /v1/completions endpoints. Point the OpenAI SDK at the local server with base_url="http://localhost:PORT/v1" and the API key set to any non-empty string. Client code is identical across engines, which makes swapping inference servers a one-line change.

How much GPU memory do I need to serve a 70B model?

At FP16 you need roughly 140 GB just for weights, plus 20–60 GB for KV cache depending on context length and batch size, so 2x80GB H100s minimum, or 4x40GB A100s. At FP8 you halve that to 70 GB weights, which fits on a single H100. At INT4 (AWQ or GPTQ) the weights drop to ~35 GB, leaving plenty of room for KV cache on one H100 or A100-80GB.

What is continuous batching in LLM serving?

Continuous batching is a scheduling technique where the inference server re-evaluates which requests are in the batch at every decode step, rather than fixing the batch for the duration of a request. This lets finished requests leave the batch immediately and new requests join mid-flight, keeping the GPU saturated even when request lengths vary wildly. All three engines (vLLM, TGI, SGLang) support it.

Arjun Krishnamurthy
About the Author Arjun Krishnamurthy

ML engineer focused on getting models out of notebooks and into production. Has war stories about every serving framework.