Skip to main content
Earlier we discussed why the largest fleets separate prefill from decode: the two phases stress hardware in opposite ways. A big read (prefill) moves large amounts of prompt context through memory at once, while streaming (decode) emits tokens one-by-one. Splitting those roles into dedicated pools lets you tune prefill and decode independently — in practice this can yield significantly higher tokens-per-second on the same hardware. Long prompts, mixed traffic, and lock-step expert groups are workloads where the split delivers the most benefit.
Splitting prefill and decode typically improves throughput because prefill optimizes bulk memory access and cache warm-up, while decode optimizes per-token latency and streaming behavior.
Why not run a full hardware-backed split in this demo? A true, fully disaggregated P/D system depends on extremely fast pod-to-pod links and memory-visible transfers across hosts — hardware our small CPU cluster doesn’t provide. Instead of faking a hardware topology, we inspect what the cluster actually runs, read the real P/D manifests, and show how close a control-plane + software P/D path can get to a full split. The code, labels, sidecars, and scheduler wiring are all present; we simply avoid claiming hardware-level transfer performance we don’t have. Start by listing pod labels in the llm-d namespace:
Look at the label on the model pods: llm-d.ai/role=decode. Every model pod currently uses that value — a single fleet handling both prompt-reading and streaming.
One fleet, no dedicated prefill specialist — every pod both reads prompts and streams answers. That llm-d.ai/role label is not cosmetic; it’s the selector slot we use to split responsibilities when we want to. At a high level, running a true P/D split requires two deployments (two fleets): one for prefill and one for decode. Our decode deployment is the default fleet already running. The P/D path simply adds a prefill deployment beside it. Folder structure (guides/recipes excerpt):
Each deployment sets the same label key to a different value. In the prefill manifest you will see llm-d.ai/role: prefill; the decode manifest keeps llm-d.ai/role: decode. The model software is the same — the role label assigns different jobs.
Second: the decode pods carry a small routing-proxy sidecar added by a patch. That sidecar acts as the receiving dock for saved work coming from a prefill pod.
A dark-themed diagram titled "A Passenger in Every Decode Pod" showing a Decode Pod with a routing-proxy sidecar ("The receiving dock") connected to a vLLM container. It also shows a patch-sidecar.yaml file noted as "Added by one patch."
The sidecar wiring is explicit: it listens on the pod’s usual front door (port 8000) while vLLM moves to port 8200. This makes the sidecar capable of proxying normal client traffic and also accepting incoming KV blocks destined for the model.
Third: both deployments belong to a single inference pool (the same chart family as the quickstart release). The pool selector does not match on the llm-d.ai/role label; it matches on a shared llm-d.ai/guide label. That allows one router to see a single fleet composed of both prefill and decode pods.
Fourth: the decision brain. The P/D configuration is an EPP (Endpoint Picker) plugin file with two scheduling profiles — prefill and decode. Filters read the llm-d.ai/role label and score candidates differently. A decider plugin can mark appropriate requests as “split”, then the prefill profile favors hosts with beneficial cache characteristics while the decode profile favors active-request scores suited for streaming.
Here’s the exact scheduling-profile scoreboard used in the cluster. The prefill profile weights prefix cache utilization, queue depth, and KV cache utilization more heavily; decode favors active-request scoring plus prefix-cache weight to optimize streaming.
Now visualize the runtime flow: a long request hits the gateway. The EPP does double duty — it selects a prefill pod to ingest the full prompt and produce saved work (KV blocks), and a decode pod to receive that saved work and stream the answer token-by-token to the client. The prefill pod consumes the prompt in a single burst and emits KV blocks (saved state). Those blocks are transferred to the chosen decode pod according to the KV transfer connector configured in the prefill deployment’s vLLM arguments.
What is NIXL? It’s NVIDIA’s inference transfer (Xfer) library — an open-source, vendor-agnostic data-movement library designed to accelerate point-to-point transfers for distributed AI inference workloads, especially moving large KV caches and supporting disaggregated LLM architectures. For more about Kubernetes networking and orchestration concepts used here, see Kubernetes Basics.
An infographic titled "What Is NIXL?" describing NVIDIA Inference Xfer (Transfer) Library with icons and feature boxes labeled "Open source," "Vendor-agnostic," and "Point-to-point." Below it are schematic blocks showing prefill and decode pod memory connected by a "Large KV caches" arrow.
The prefill keeps --block-size=128 so KV payloads are chunked predictably. The decode-side sidecar receives KV blocks; the decode pod resumes from the saved state and streams tokens back via the original connection.
A dark-themed architecture diagram titled "The Stream Comes Back" showing an inference gateway routing user requests to Prefill Pods and Decode Pods (vLLM). It depicts saved work between pods and a sidecar streaming the answer back token-by-token to the user.
Summary — the pieces you can inspect on your cluster:
  • Role labels (llm-d.ai/role) mark prefill vs decode.
  • A routing-proxy sidecar in decode pods accepts KV transfers and proxies traffic.
  • The router/EPP uses a plugin config (pd-config.yaml) with prefill and decode scheduling profiles.
  • Prefill pods use a KV transfer connector (NixlConnector) and shard KV payloads using --block-size.
Table: key manifest pieces and purpose
A production-grade P/D split requires high-bandwidth, low-latency pod-to-pod links (or RDMA-like mechanisms). Software-only P/D shows the control-plane and routing patterns but will not match hardware-backed disaggregation without appropriate networking/hardware.
The result: prefill work happens in one place and streaming happens in another, but the manifests, labels, sidecars, and routing glue are all present in the repository and on the cluster. This control-plane + software implementation maps the pieces of a full-scale split without claiming specialized hardware-level performance. If you want to explore further, inspect the guides/recipes/*/pd manifests and the pd-config.yaml in your quickstart chart to see the exact wiring used by the cluster.

Watch Video