1. Pick a model that fits
| GPU memory | Fits comfortably (with KV cache room) |
|---|---|
| 24 GB (L4, RTX 4090) | 7–8B in BF16, 14B in FP8/AWQ |
| 48 GB (L40S) | 14B in BF16, 32B in FP8/AWQ |
| 80 GB (A100/H100) | 32B in BF16, 70B in INT4/AWQ (tight) |
Leave 20–40% of memory for the KV cache. That memory is what lets the server handle many concurrent requests.
2. Drivers and container runtime
sudo ubuntu-drivers install # or install the NVIDIA datacenter driver
sudo apt-get install -y nvidia-container-toolkit
sudo nvidia-ctk runtime configure --runtime=docker && sudo systemctl restart docker
nvidia-smi
3. Run vLLM (OpenAI-compatible)
docker run -d --name vllm --gpus all --ipc=host -p 127.0.0.1:8000:8000 \
-v /srv/hf-cache:/root/.cache/huggingface \
-e HF_TOKEN=$HF_TOKEN \
--restart unless-stopped \
vllm/vllm-openai:latest \
--model Qwen/Qwen2.5-14B-Instruct-AWQ \
--served-model-name chat-default \
--max-model-len 16384 \
--gpu-memory-utilization 0.90 \
--max-num-seqs 64 \
--enable-prefix-caching \
--api-key "$VLLM_API_KEY"
Pin a specific image tag in production instead of latest.
| Flag | Why |
|---|---|
--max-model-len | Caps context. Lower values leave more KV cache for concurrent requests |
--gpu-memory-utilization | Share of GPU memory vLLM may use |
--max-num-seqs | Upper bound on concurrent sequences |
--enable-prefix-caching | Reuses shared prompt prefixes (system prompts, RAG templates) |
--served-model-name | A stable name for clients, so changing the model doesn't break them |
4. Test
curl -s http://127.0.0.1:8000/v1/chat/completions \
-H "Authorization: Bearer $VLLM_API_KEY" -H "Content-Type: application/json" \
-d '{"model":"chat-default","messages":[{"role":"user","content":"Say hello"}],"max_tokens":20}'
5. Expose it safely
vLLM listens only on localhost. Put Nginx or Caddy in front, with TLS, and restrict it to the app subnet:
server {
listen 443 ssl;
server_name llm01.corp.local;
ssl_certificate /etc/ssl/llm01.crt;
ssl_certificate_key /etc/ssl/llm01.key;
allow 10.20.0.0/16; deny all;
location /v1/ {
proxy_pass http://127.0.0.1:8000;
proxy_read_timeout 300s;
proxy_buffering off; # streaming
}
}
Better still: route apps through your AI gateway, which handles keys and per-team limits.
6. Monitoring
vLLM exposes Prometheus metrics at /metrics: vllm:num_requests_running, vllm:num_requests_waiting, vllm:kv_cache_usage_perc (named gpu_cache_usage_perc in older versions), time to first token and generation throughput. Add the DCGM exporter for GPU temperature, memory and errors.
7. Load test before go-live
vllm bench serve --backend openai-chat --base-url http://127.0.0.1:8000 \
--endpoint /v1/chat/completions --model chat-default \
--dataset-name random --random-input-len 2000 --random-output-len 300 \
--num-prompts 300 --max-concurrency 32
(Older versions use benchmarks/benchmark_serving.py.) Record tokens per second and p95 time to first token at your expected concurrency.
8. What "standalone" means here
One GPU, one server: a restart or GPU fault means downtime. Fine for internal tools and pilots. For production SLOs, see the HA self-hosted inference post.
Tip
If requests queue up (
num_requests_waiting> 0 for a long time) while GPU memory is full, reduce--max-model-lenor use a smaller quantisation before buying a bigger GPU.