A single NVIDIA A100 can serve a 13B model to dozens of concurrent users or to exactly one, and the difference is entirely how you run it. The short version of this guide: install vLLM, point vllm serve at a Hugging Face model id, and you get an OpenAI-compatible endpoint on port 8000 that batches incoming requests for you. vLLM's PagedAttention algorithm holds memory waste under 4% and, in the project's launch benchmarks, delivered up to 24x the throughput of raw Hugging Face Transformers. What follows takes you from an idle GPU box to a production-shaped inference server, and names the handful of flags that actually decide whether it stays up.
Key takeaways
- The short answer:
uv pip install vllm, thenvllm serve <model-id>, then queryhttp://localhost:8000/v1/chat/completionslike any OpenAI endpoint. - vLLM speaks the OpenAI API, so existing OpenAI client code works after you change only the base URL.
- Two flags head off most crashes:
--gpu-memory-utilizationand--max-model-len. - Set
--max-model-lento the longest prompt plus output you truly serve, because vLLM reserves KV cache blocks for the whole window up front. - When a model won't fit on one card,
--tensor-parallel-sizesplits it across GPUs on a single node. - Add
--api-keybefore you expose the server to anything past localhost.
Check These Prerequisites First
If your goal is a model on a laptop for local tinkering, Ollama is the lighter tool and I have covered it separately in running large language models locally with Ollama. Reach for vLLM when you need concurrency, request batching, and throughput on a real GPU.
Before you start, confirm you have:
- A Linux host with an NVIDIA GPU and a current CUDA driver. vLLM also ships wheels for AMD ROCm.
- Python 3.10 through 3.13.
- Enough VRAM. A rough rule for 16-bit weights is about 2 GB per billion parameters, so a 7B model needs roughly 14 GB for weights alone before the KV cache. A 16 GB card handles small models; anything larger wants a 40 GB card or several GPUs. If GPU spend is the real constraint, I worked through that math in measuring AI infrastructure costs.
- A Hugging Face token if the checkpoint is gated. Set it up first with the steps in accessing gated models on Hugging Face.
Serve Your First Model in Four Steps
This is the whole procedure. Finish these four steps and you have a working, queryable server; everything after is tuning.
- Create an isolated environment and install vLLM. The project recommends
uv, which resolves the right Torch build for your hardware.uv venv --python 3.12 --seed source .venv/bin/activate uv pip install vllm --torch-backend=auto - Start the server against a Hugging Face model id. Begin with a small model to confirm the box works before you load anything heavy. It binds to
localhost:8000by default.vllm serve Qwen/Qwen2.5-1.5B-Instruct - Confirm the server is up and reporting the model.
curl http://localhost:8000/v1/models - Send a chat completion. The
modelfield has to match the id you served.curl http://localhost:8000/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{ "model": "Qwen/Qwen2.5-1.5B-Instruct", "messages": [ {"role": "user", "content": "Explain KV cache paging in two sentences."} ] }'
If that last call returns JSON with a completion, you are done with the hard part. The server exposes the standard OpenAI routes, including /v1/completions, /v1/chat/completions, /v1/embeddings, and /v1/models.
Point Existing OpenAI Code at Your Server
Because the API matches OpenAI's, you swap the base URL and reuse the client you already have. When you have not set a server key, any non-empty string works as api_key.
from openai import OpenAI
client = OpenAI(base_url="http://localhost:8000/v1", api_key="EMPTY")
resp = client.chat.completions.create(
model="Qwen/Qwen2.5-1.5B-Instruct",
messages=[{"role": "user", "content": "Give me one tip for load testing an inference server."}],
)
print(resp.choices[0].message.content)
Tune the Flags That Control Memory and Throughput
vLLM defaults run, but they rarely fit your GPU or your traffic. These are the flags worth knowing before you serve anything real, drawn from the engine arguments reference.
| Flag | What it does | When to reach for it |
|---|---|---|
--gpu-memory-utilization | Fraction of GPU memory (0 to 1) given to the model executor and KV cache. | Lower it (for example 0.85) when the GPU is shared; raise it toward 0.95 to squeeze in a longer context. |
--max-model-len | Maximum context length, prompt plus output. | Set it to your real longest request. Lowering it is the fastest cure for startup out-of-memory. |
--tensor-parallel-size | Number of GPUs to shard one model across on a single node. | Any model too large for one card. |
--quantization | Weight quantization method, such as awq or gptq. | Halving weight VRAM to fit a bigger model on the same card. |
--max-num-seqs | Maximum sequences processed in one iteration. | Cap concurrency when heavy load triggers out-of-memory mid-run. |
--api-key | Requires callers to present a matching key in the header. | Always, before the port is reachable off localhost. |
--served-model-name | Renames the model as clients see it in the API. | Giving callers a stable alias instead of a long repo id. |
A memory-safe starting command for a 7B model on a single 24 GB card looks like this:
vllm serve Qwen/Qwen2.5-7B-Instruct \
--gpu-memory-utilization 0.90 \
--max-model-len 8192 \
--api-key sk-local-your-secret
Scale a Model Past a Single GPU
When the weights don't fit on one card, tensor parallelism splits the model across several GPUs on the same node. Set --tensor-parallel-size to the number of GPUs you want to use, and vLLM handles the sharding.
vllm serve meta-llama/Llama-3.1-70B-Instruct \
--tensor-parallel-size 4
The parallelism and scaling guide covers the case where a model is too large for even one node: you combine tensor parallel with --pipeline-parallel-size across machines. For a single box, tensor parallel is the one you want.
Troubleshoot the Errors You'll Actually Hit
Almost every early failure traces back to memory or to a mismatched request. Here are the ones you will meet and what fixes each.
| Symptom | Likely cause | Fix |
|---|---|---|
CUDA out of memory at startup | KV cache reservation exceeds free VRAM. | Lower --max-model-len to your real prompt plus output length, then lower --gpu-memory-utilization, then try a quantized checkpoint or --quantization awq. |
CUDA out of memory only under load | Too many concurrent sequences. | Lower --max-num-seqs. |
401 or gated repo while pulling the model | No Hugging Face token for a gated checkpoint. | Run huggingface-cli login or set HF_TOKEN. See the gated models guide. |
model ... does not exist from the API | The request model field does not match what you served. | Use the exact repo id, or set --served-model-name and call that name. |
Address already in use | Port 8000 is taken. | Pass --port with a free port. |
| First request slow, later ones fast | CUDA graph capture during warmup. | Expected. Send a warmup request, or use --enforce-eager to trade some speed for lower memory. |
The single most useful habit: size --max-model-len to the traffic you actually serve. vLLM reserves KV cache blocks for the full window, so every token of headroom you never use is VRAM you paid for and wasted.
Move the Server Toward Production
A working localhost server is the start, not the finish. From here:
- Put the server behind a reverse proxy or gateway, and keep
--api-keyset. Never expose the raw port to a network you do not control. - Fit a larger model on the same hardware with quantization. The quantization guide lists the supported methods and their trade-offs.
- Load test before you promise anyone a latency number. Measure tokens per second and time to first token at your real concurrency, not at one request.
- Right-size the model itself. A smaller model that clears your quality bar serves more users per GPU, which is the argument I made in when to use small language models in agentic systems.
If you are weighing a serving stack for a system you own and want a second opinion, my contact details are on the homepage.