If you're serving large language models (LLMs) on Kubernetes today, your inference stack probably looks something like this: vLLM or TGI behind a standard load balancer, with requests distributed round-robin across your GPU-backed replicas. It works. Until you start scaling and realize that every follow-up message in a multi-turn conversation is recomputing prefixes that were already computed seconds ago on a different pod.
That's the problem llm-d solves. It's an open source, Kubernetes-native inference routing layer that sits in front of your model servers and makes intelligent decisions about where to send each request. Prefix-cache-aware routing, load-aware balancing, predicted-latency optimization. All built on top of the Gateway API InferencePool, so you're not locked into any proprietary routing setup.
In this post, we'll walk through a full llm-d deployment from scratch in under five minutes. No slides, no theory. Just a working stack on Red Hat OpenShift with a GPU, a model, and a router.
Before you get started, you'll need:
llm-d's routing layer is built on the Kubernetes Gateway API and the Inference Extension API. You need both sets of Custom Resource Definitions (CRDs) installed before anything else.
oc apply -k "https://github.com/kubernetes-sigs/gateway-api/config/crd/?ref=v1.5.1" oc apply -k "https://github.com/kubernetes-sigs/gateway-api-inference-extension/config/crd/?ref=v1.5.0"
These CRDs give you access to the InferencePool and InferenceModel resources, which are how the llm-d router discovers and communicates with your model servers.
llm-d supports multiple gateway providers, including Envoy and AgentGateway. For this walkthrough, we're using AgentGateway because it's a single Helm install.
helm install agentgateway oci://ghcr.io/agentgateway/charts/agentgateway \ --version v1.1.0 \ --namespace agentgateway-system \ --create-namespace
Then apply the gateway configuration from the llm-d repository:
oc apply -k guides/recipes/gateway/agentgateway/
At this point, the gateway is running and ready to accept routing rules.
The model server is standard vLLM running behind an llm-d deployment configuration. The llm-d repository includes nine pre-tested deployment recipes called well-lit paths that cover common hardware and model configurations. We're using the optimized-baseline path here.
oc apply -f vllm-deploy.yaml
The deployment YAML configures vLLM with the appropriate GPU tolerations, resource requests, and volume mounts for cache directories. It also includes the label llm-d.ai/guide: optimized-baseline, which is how the InferencePool selector finds the pod.
The model takes roughly 60 to 90 seconds to download and load into GPU memory. Once vLLM reports Application startup complete in the logs, it's ready to serve.
The router is the component that makes llm-d more than just vLLM on Kubernetes. It's deployed as a Helm chart:
helm install optimized-baseline \ oci://ghcr.io/llm-d/charts/llm-d-router-standalone-dev \ -f guides/recipes/router/base.values.yaml \ -f guides/optimized-baseline/router/optimized-baseline.values.yaml \ -n llm-d-demo --version v0
After this, you should see two pods running: the vLLM model server and the llm-d router (EPP). You can verify the router has discovered the model server by checking the InferencePool:
oc get inferencepool -n llm-d-demo
That's it. Four steps. The full llm-d stack is deployed and ready to route inference requests.
To understand what the router adds, it helps to see both paths side by side.
Port-forward to the model server and send a standard OpenAI-compatible request:
oc port-forward deploy/vllm-decode 8000:8000 -n llm-d-demo &
curl -s http://localhost:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"model":"Qwen/Qwen3-0.6B",
"messages":[{"role":"user","content":"Explain Kubernetes in three sentences."}],
"max_tokens":150}' | python3 -m json.tool
This works exactly as expected. You get a response, and it's identical to what you'd see from any standalone vLLM deployment.
Now send the same request through the router:
oc port-forward svc/optimized-baseline-epp 8080:80 -n llm-d-demo &
curl -s http://localhost:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"model":"Qwen/Qwen3-0.6B",
"messages":[{"role":"user","content":"Explain Kubernetes in three sentences."}],
"max_tokens":150}' | python3 -m json.tool
The response looks the same. But under the hood, the router has cached the prefix of this conversation. When you send a follow-up message, the router knows which backend already has the key-value (KV) cache for this conversation and routes the request there instead of recomputing it from scratch.
With a single replica, the benefit is subtle. But in production, where you might have 10, 20, or more model server replicas handling hundreds of concurrent multi-turn conversations, the difference is significant.
Without llm-d, each follow-up message could land on any random replica and recompute the entire conversation prefix. With llm-d's prefix-cache-aware routing, the follow-up goes to the replica that already has the context cached. That means less GPU compute per request, higher throughput on the same hardware, and lower cost per token served.
This walkthrough used a 0.6B model on a single GPU to keep things quick. But the same deployment pattern scales to much larger configurations:
llm-d is an open source project under the Cloud Native Computing Foundation (CNCF) sandbox. The full source, documentation, and well-lit path deployment guides are available in the llm-d repository on GitHub.
If you're already running vLLM in production and want to add intelligent routing without rearchitecting your stack, llm-d is designed to drop in with minimal changes. The well-lit paths provide tested configurations for common GPU and model combinations, so you can go from zero to a working deployment in minutes.
Here's how to take the next step:
A fully styled preview of this blog post (with all diagrams, code formatting, and layout) is available as a standalone HTML file:
File: llm-d-deploy-5-minutes-blog-updated.html (same folder as this submission)
Open it in any browser to see the intended final design with Red Hat fonts, styled code blocks, inline callouts, and the CTA section. Use Cmd+P / Ctrl+P to export as PDF if needed.
| Kubernetes explainer | redhat.com/en/topics/containers/what-is-kubernetes |
| vLLM project | github.com/vllm-project/vllm |
| llm-d GitHub repo | github.com/llm-d/llm-d |
| Gateway API docs | gateway-api.sigs.k8s.io |
| Inference Extension API (GEP) | gateway-api.sigs.k8s.io/geps/gep-1897 |
| Red Hat OpenShift | redhat.com/en/technologies/cloud-computing/openshift |
| Red Hat AI | redhat.com/en/products/ai |
| CNCF | cncf.io |
| "What is llm-d?" blog | redhat.com/en/blog/what-llm-d-and-why-do-we-need-it |
| llm-d Slack channel | cloud-native.slack.com/archives/llm-d |