Deploying Quantized Models with Hugging Face TGI
Large language models (LLMs) have transformed how developers build AI-powered applications, but their sheer size makes production deployment a serious engineering challenge. A 70-billion-parameter model in FP16 precision requires roughly 140 GB of GPU memory — well beyond what a single consumer or mid-tier datacenter GPU can offer. Quantization, combined with Hugging Face's Text Generation Inference (TGI) server, offers a practical path to running these models efficiently on accessible hardware without sacrificing meaningful quality.
What Is Hugging Face TGI?
Text Generation Inference (TGI) is a Rust- and Python-based inference server developed by Hugging Face specifically for serving LLMs in production. Unlike generic serving frameworks, TGI is purpose-built for text generation workloads and ships with optimizations that make it a popular choice for enterprise deployments.
Key capabilities of TGI include:
- Continuous batching — dynamically groups incoming requests to maximize GPU utilization.
- PagedAttention — manages the KV cache like virtual memory, reducing fragmentation and waste.
- Tensor parallelism — splits model weights across multiple GPUs for models too large for one device.
- Built-in quantization support — integrates with popular backends like bitsandbytes, GPT-Q, AWQ, and EETQ.
- OpenAI-compatible REST API — drop-in replacement for many existing client libraries.
- Token streaming — server-sent events for real-time token delivery.
What Is Model Quantization?
Quantization is the process of reducing the numerical precision used to represent a model's weights (and sometimes activations). Instead of storing each weight as a 16-bit or 32-bit floating-point number, quantization represents them using 8-bit, 4-bit, or even 2-bit integers. This dramatically reduces memory footprint and often improves inference speed because lower-precision math is faster on modern GPUs.
The main quantization formats you will encounter when working with TGI are:
- bitsandbytes (NF4 / 8-bit) — on-the-fly quantization, easy to apply, slight quality and speed tradeoff.
- GPTQ — post-training quantization that minimizes output error per layer; produces 4-bit models with good accuracy.
- AWQ (Activation-aware Weight Quantization) — preserves the most salient weights based on activation magnitudes; often faster than GPTQ.
- EETQ — 8-bit weight quantization with very low overhead, great throughput on Ampere and newer GPUs.
- FP8 (via vLLM-style kernels) — supported on Hopper (H100) GPUs for maximum performance.
Why Quantization Matters in Production
For most teams, the choice is not between "quantized" and "unquantized" — it is between "quantized and deployable" and "unquantized and unaffordable." Quantization matters for several concrete reasons:
- Memory reduction — a 4-bit quantized model uses roughly 25% of the memory of its FP16 counterpart, allowing a 70B model to fit on a single 80 GB GPU.
- Lower hardware cost — running on one A10G instead of four A100s can reduce cloud spend by an order of magnitude.
- Higher throughput — smaller weights mean less memory bandwidth pressure, which is usually the bottleneck for autoregressive decoding.
- Lower latency — faster token generation improves user experience for chat and assistant applications.
- Greener deployments — fewer GPUs means lower energy consumption per request.
The tradeoff is a small degradation in output quality. For most conversational and summarization tasks, 4-bit quantization is virtually indistinguishable from full precision. For math, code, or highly precise reasoning tasks, you may want to benchmark carefully before committing.
Prerequisites
Before deploying a quantized model with TGI, ensure you have the following:
- A machine with at least one NVIDIA GPU (Ampere architecture or newer recommended).
- Docker installed with NVIDIA Container Toolkit configured.
- Sufficient GPU memory for your chosen model (we will cover sizing below).
- A Hugging Face access token if you plan to serve gated models such as Llama 3 or Mistral variants.
Verify your GPU is visible to Docker:
docker run --rm --gpus all nvidia/cuda:12.2.0-base-ubuntu22.04 nvidia-smi
Deploying a bitsandbytes-Quantized Model
The simplest way to get started is to let TGI quantize a model on the fly using bitsandbytes. This requires no pre-quantized weights — you point TGI at any standard model repository and add a single flag.
docker run --gpus all --shm-size 1g -p 8080:80 \
-e HF_TOKEN=hf_your_token_here \
ghcr.io/huggingface/text-generation-inference:latest \
--model meta-llama/Meta-Llama-3-8B-Instruct \
--quantize bitsandbytes \
--max-input-length 4096 \
--max-total-tokens 8192
The --quantize bitsandbytes flag instructs TGI to load weights in 4-bit NF4 format. This is ideal for quick prototyping and works with almost any model on the Hub. The downside is that on-the-fly quantization adds startup time and the bitsandbytes kernel is generally slower than GPTQ or AWQ for inference.
Deploying a Pre-Quantized GPTQ Model
For better inference performance, use a model that has already been quantized using GPTQ. The Hugging Face Hub hosts many community-quantized checkpoints. TGI detects the quantization config from the model repository automatically.
docker run --gpus all --shm-size 1g -p 8080:80 \
-e HF_TOKEN=hf_your_token_here \
ghcr.io/huggingface/text-generation-inference:latest \
--model TheBloke/Mistral-7B-Instruct-v0.2-GPTQ \
--quantize gptq \
--max-input-length 4096 \
--max-total-tokens 8192
Notice the --quantize gptq flag. TGI will load the pre-quantized safetensors and use optimized GPTQ kernels during inference. Startup is faster than bitsandbytes because no quantization happens at load time.
Deploying an AWQ-Quantized Model
AWQ is often the best choice for production because it tends to offer higher throughput than GPTQ while maintaining comparable accuracy. Deployment is nearly identical:
docker run --gpus all --shm-size 1g -p 8080:80 \
-e HF_TOKEN=hf_your_token_here \
ghcr.io/huggingface/text-generation-inference:latest \
--model casperhansen/llama-3-70b-instruct-awq \
--quantize awq \
--max-input-length 4096 \
--max-total-tokens 8192
A 70B AWQ model typically fits in around 40 GB of GPU memory, making it deployable on a single A100 80GB or two A6000 GPUs with tensor parallelism.
Using EETQ for 8-Bit Inference
If you want a middle ground between full precision and aggressive 4-bit quantization, EETQ provides high-throughput 8-bit weight-only quantization with negligible quality loss. It is especially effective on Ampere and Hopper GPUs.
docker run --gpus all --shm-size 1g -p 8080:80 \
-e HF_TOKEN=hf_your_token_here \
ghcr.io/huggingface/text-generation-inference:latest \
--model meta-llama/Meta-Llama-3-8B-Instruct \
--quantize eetq \
--max-input-length 4096 \
--max-total-tokens 8192
EETQ quantizes weights on the fly at load time, so startup is slower than using a pre-quantized checkpoint but faster than bitsandbytes for actual inference.
Multi-GPU Deployment with Tensor Parallelism
For models that exceed the memory of a single GPU, TGI supports tensor parallelism via the --num-shard flag. This splits the model across GPUs and is fully compatible with quantized weights.
docker run --gpus all --shm-size 1g -p 8080:80 \
-e HF_TOKEN=hf_your_token_here \
ghcr.io/huggingface/text-generation-inference:latest \
--model casperhansen/llama-3-70b-instruct-awq \
--quantize awq \
--num-shard 2 \
--max-input-length 4096 \
--max-total-tokens 8192
This command splits the 70B AWQ model across two GPUs. Make sure both GPUs have identical memory capacity and architecture for best performance.
Querying the TGI Server
Once TGI is running, you can interact with it using a simple HTTP client. The server exposes both a native API and an OpenAI-compatible endpoint at /v1/chat/completions.
Using the OpenAI-compatible endpoint with Python:
from openai import OpenAI
client = OpenAI(
base_url="http://localhost:8080/v1",
api_key="not-needed-for-local"
)
response = client.chat.completions.create(
model="tgi",
messages=[
{"role": "system", "content": "You are a helpful coding assistant."},
{"role": "user", "content": "Explain what a KV cache is in two sentences."}
],
max_tokens=200,
temperature=0.7,
stream=True
)
for chunk in response:
if chunk.choices[0].delta.content is not None:
print(chunk.choices[0].delta.content, end="")
Using the native TGI API with curl:
curl -X POST http://localhost:8080/generate \
-H "Content-Type: application/json" \
-d '{
"inputs": "What are the benefits of model quantization?",
"parameters": {
"max_new_tokens": 256,
"temperature": 0.7,
"top_p": 0.9,
"do_sample": true
}
}'
Monitoring and Health Checks
TGI exposes a health endpoint and a metrics endpoint that you should integrate into your deployment pipeline. The metrics endpoint is Prometheus-compatible and exposes counters for request latency, queue depth, and GPU utilization.
# Health check
curl http://localhost:8080/health
# Prometheus metrics
curl http://localhost:8080/metrics
Useful metrics to watch include tgi_request_duration_seconds, tgi_queue_size, and tgi_request_count. These help you decide when to scale horizontally by adding more TGI replicas behind a load balancer.
Best Practices for Production Deployments
Based on real-world experience deploying quantized models with TGI, the following practices consistently lead to better outcomes:
- Choose AWQ over GPTQ when possible — AWQ kernels generally deliver higher throughput on Ampere and Hopper GPUs, and the quality difference is negligible for most tasks.
- Pre-quantize rather than quantize on the fly — uploading a pre-quantized checkpoint to your own Hub repository reduces cold-start time and ensures reproducible deployments.
- Set
--max-total-tokenscarefully — this controls the size of the KV cache allocation. Setting it too high wastes memory; too low truncates outputs. Match it to your actual workload. - Use
--shm-size 1gor higher — TGI uses shared memory for inter-process communication; the default Docker allocation is often insufficient. - Pin your image version — avoid
:latestin production. Pin to a specific TGI release tag to prevent regressions when upstream kernels change. - Benchmark before committing — run representative prompts through both the quantized and unquantized versions of your model and compare outputs. Use eval harnesses like
lm-evaluation-harnessfor systematic comparison. - Enable prefix caching — for chat workloads with repeated system prompts, prefix caching dramatically reduces time-to-first-token. Pass
--prefix-cache-lento enable it. - Reserve GPU memory for overhead — do not assume a model that "fits" in 40 GB will run on a 40 GB GPU. Leave 10–15% headroom for the KV cache, CUDA context, and framework overhead.
- Use a reverse proxy with timeouts — TGI can hold long-lived streaming connections. Configure nginx or Envoy with appropriate timeout values to avoid dropped streams.
- Log and alert on queue depth — a growing
tgi_queue_sizeis the earliest signal that your deployment is under-provisioned.
Common Pitfalls and How to Avoid Them
Even with a solid understanding of TGI and quantization, a few issues recur frequently:
- Out-of-memory errors at startup — usually caused by
--max-total-tokensbeing too high for the available KV cache memory. Reduce it or add another GPU shard. - Slow first-token latency — often a sign that prefix caching is disabled or that the system prompt is very long. Enable prefix caching and consider trimming system prompts.
- Garbled outputs with GPTQ — some GPTQ checkpoints use group sizes or act-order settings incompatible with certain TGI versions. Try an AWQ version of the same model or update TGI.
- Permission denied on gated models — ensure your
HF_TOKENhas read access to the model repository and that you have accepted any model license agreements on the Hub. - Container crashes with no logs — almost always a shared memory issue. Increase
--shm-sizeto at least 1 GB.
A Complete Docker Compose Example
For production deployments, Docker Compose provides a clean way to manage configuration, environment variables, and restart policies. Below is a complete example you can adapt:
version: "3.9"
services:
tgi:
image: ghcr.io/huggingface/text-generation-inference:2.3.0
container_name: tgi-server
restart: unless-stopped
ports:
- "8080:80"
environment:
- HF_TOKEN=${HF_TOKEN}
- HUGGING_FACE_HUB_CACHE=/data
volumes:
- ./hf-cache:/data
shm_size: 1g
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu]
command:
- --model=casperhansen/llama-3-70b-instruct-awq
- --quantize=awq
- --num-shard=2
- --max-input-length=4096
- --max-total-tokens=8192
- --max-batch-prefill-tokens=8192
- --prefix-cache-len=4096
- --log-level=info
Start the stack with:
export HF_TOKEN=hf_your_token_here
docker compose up -d
Conclusion
Deploying quantized models with Hugging Face TGI is one of the most effective ways to bring large language models into production without breaking your hardware budget. By understanding the tradeoffs between bitsandbytes, GPTQ, AWQ, and EETQ, you can choose the right quantization strategy for your specific latency, throughput, and quality requirements. Combined with TGI's continuous batching, PagedAttention, and tensor parallelism, even 70-billion-parameter models become practical to serve on commodity GPUs. Start with a pre-quantized AWQ checkpoint, benchmark it against your real workloads, and iterate on configuration parameters like --max-total-tokens and prefix caching until you hit your target performance. With careful tuning and the practices outlined above, you can build inference infrastructure that is both cost-efficient and production-grade.