Serving an open-weights model on your own cluster is the foundation of the platform-for-AI stack — it's what makes client-safe AI, cost control, and everything downstream (gateways, observability, guardrails) possible.
Tier: Advanced · Workflow area: Platform-for-AI: serving & gateways
This playbook deploys vLLM as a proper Kubernetes service: OpenAI-compatible API, GPU scheduling, health probes, model cache, and the serving concepts (prefill vs decode, KV cache, continuous batching) you need to reason about its performance.
Skip self-hosting when usage is low and data policy permits hosted APIs — a gateway in front of hosted providers (pb-45) is far less operational burden.
kubectl describe node | grep nvidia.com/gpu shows capacity). No GPU? See the free-tier paths under Tool variants.Start with a model that fits. Weights-plus-KV-cache must fit GPU memory: a 7–8B model in FP16 needs ~16GB plus cache headroom. Use a quantized (AWQ/GPTQ) variant on 16GB cards. Ask your assistant to sanity-check: "vRAM budget for Qwen2.5-7B-Instruct FP16 on one L4 with 8k context, ~8 concurrent requests?" — then verify against vLLM docs.
Write the Deployment. Have your assistant draft it, then check every field below exists — these are the parts assistants routinely omit:
apiVersion: apps/v1
kind: Deployment
metadata: {name: vllm-qwen, labels: {app: vllm-qwen}}
spec:
replicas: 1
selector: {matchLabels: {app: vllm-qwen}}
template:
metadata: {labels: {app: vllm-qwen}}
spec:
containers:
- name: vllm
image: vllm/vllm-openai:latest # pin a version in prod
command: ["vllm", "serve", "Qwen/Qwen2.5-7B-Instruct",
"--max-model-len", "8192"]
env:
- name: HUGGING_FACE_HUB_TOKEN
valueFrom: {secretKeyRef: {name: hf-token, key: token}}
ports: [{containerPort: 8000}]
resources:
limits: {nvidia.com/gpu: "1", memory: 20Gi, cpu: "8"}
requests: {nvidia.com/gpu: "1", memory: 8Gi, cpu: "2"}
readinessProbe:
httpGet: {path: /health, port: 8000}
initialDelaySeconds: 60
periodSeconds: 5
livenessProbe:
httpGet: {path: /health, port: 8000}
initialDelaySeconds: 60
periodSeconds: 10
volumeMounts:
- {name: hf-cache, mountPath: /root/.cache/huggingface}
- {name: shm, mountPath: /dev/shm}
volumes:
- name: hf-cache
persistentVolumeClaim: {claimName: vllm-hf-cache}
- name: shm
emptyDir: {medium: Memory, sizeLimit: 2Gi}
The PVC cache means restarts don't re-download 15GB of weights; the /dev/shm emptyDir is required for tensor-parallel and NCCL. Set generous probe delays — model load takes minutes.
Expose it privately. ClusterIP Service on port 8000. Do not put an Ingress on it — the gateway (pb-44/pb-45) is the front door; raw model endpoints must not be directly reachable by clients.
Smoke-test the OpenAI-compatible API.
kubectl port-forward svc/vllm-qwen 8000:8000
curl -s localhost:8000/v1/chat/completions -H 'Content-Type: application/json' \
-d '{"model":"Qwen/Qwen2.5-7B-Instruct","messages":[{"role":"user","content":"Say OK"}],"max_tokens":8}'
Scrape metrics and watch the two numbers that matter. vLLM exposes Prometheus metrics at /metrics. Chart vllm:num_requests_waiting (queue depth — your scaling signal) and GPU cache usage. HPA on CPU is meaningless for LLM pods; scale on queue depth or concurrency via custom metrics.
Kill it and watch it recover. Delete the pod; confirm probes gate traffic until the model is loaded and no requests hit an unready pod. This failure drill is your acceptance test.
An LLM pod is stateful-ish and slow to start — the probes, cache PVC, and queue-depth scaling signal are what distinguish a service from a demo.
curl /v1/chat/completions returns a completion; /health returns 200; /metrics scrapes.num_requests_waiting) rather than crash./dev/shm, HF token secret, probe delays long enough for model load. Diff against the checklist in step 2.--max-model-len defaulting huge. The default context length can eat your KV-cache budget and tank concurrency; set what your workloads need.latest image in prod. vLLM moves fast; pin versions and test upgrades — flags and defaults change between releases.Self-hosting is the restricted-environment play — the model runs inside your (or the client's) VPC and prompts never leave. Complete it properly:
Check the model's license (Llama community license, Qwen/Apache-2.0, etc.) against commercial use in a client deliverable before you standardize on it.