Skip to main content
The EPP (the routing “brain”) scores every pod and picks the winner for each incoming request. But where do those scores come from? You might expect a central telemetry agent or exotic reporting system, but the truth is simpler: the routing decision is built from two very ordinary Kubernetes primitives — an API watch and a metrics scrape — combined into a compact data layer. Underneath all three stages sits that data layer, and it has exactly two jobs:
  • Discover which pods exist for a given model.
  • Observe what those pods are doing right now.
These two pieces together create the cluster’s live view that the EPP uses to score and pick a pod.
A dark-themed architecture diagram titled "Job 1 – Which Pods Exist" showing an API Server, a Controller and a Data Layer querying an InferencePool. Arrows indicate data flow between the API Server/controller and the InferencePool.
There is no static list for you to edit — the cluster is the source of truth. A small custom object called the InferencePool advertises which pods are serving a particular model; a controller watches the Kubernetes API server and keeps that list current. The data layer therefore maintains the current set of pod endpoints, for example:
The second job is to know what those pods are doing at any instant. Each serving pod exposes a lightweight metrics endpoint; runtimes such as vLLM already publish these metrics. A typical pod reports an instantaneous snapshot like this:
Your existing monitoring may already be scraping this same endpoint.
A dark-themed diagram titled "The Live View" showing a monitoring icon on the left linked by a dotted line to a bordered "Decode Pool" on the right containing multiple rectangular nodes with small blue container icons. The left label reads "Your monitoring" and the right section is labeled "Decode Pool."
The EPP continually reads those metrics from every pod in the pool. In practice this is just two simple loops working together:
  • A controller watches the Kubernetes API server to discover pod endpoints (the InferencePool).
  • A scraper loop polls each pod’s /metrics endpoint to capture its instantaneous state.
That API-watch plus metrics-scrape is the entire live view of the fleet — two basic Kubernetes primitives solving a load-balancing problem that plain L4/L7 load balancers cannot resolve by themselves.
A dark-themed architecture diagram titled "Two Ordinary Pieces" showing two numbered boxes: (1) a control-manager talking to a Kubernetes API server and (2) a service exposing a /metrics endpoint. Both components are shown feeding into a central Load Balancer at the bottom.
One piece remains: the gateway that accepts client connections. That gateway typically runs an L7 proxy — commonly Envoy — which actually holds the client connection. The EPP runs in a separate pod, so the proxy and the EPP need a way to exchange routing decisions without shunting the request body through the EPP. They use Envoy’s ext_proc (external processing) hook. Ext_proc is a built-in Envoy feature that lets the proxy pause a request, call out to an external service, and use that service’s response to influence the proxy’s next action. The router simply plugs its decision into this existing hook — no custom proxy protocol is required.
A dark-themed diagram titled "A Hook Envoy Already Had" showing an L7 proxy (Envoy) inside a gateway connected to an EPP component, with separate boxes representing pods (EPP and pod 0).
Important: the EPP is not a hop in your request’s data path. The sequence is:
  1. The L7 proxy (Envoy) receives the client request and pauses processing.
  2. Envoy sends a small routing query (metadata about the request) to the EPP via ext_proc.
  3. The EPP returns a pod name or endpoint.
  4. Envoy forwards the original request directly to the chosen pod.
  5. The pod response streams back to the client through Envoy.
So tokens never travel through the EPP — only a tiny routing query does.
A diagram titled "Tokens Never Touch the EPP" showing a user request routed through an L7 Envoy proxy to a pod, with an EPP component depicted above the proxy and purposely not connected (marked with an X). Green connection dots along the path are labeled "the answer."
The EPP only receives a tiny routing query (metadata about the request) and returns an endpoint. It never handles the request body or response stream, which keeps the EPP lightweight and low-latency.
This architecture explains why the routing brain can stay small: it never moves gigabytes of tokens — it only answers a quick question. Every scoring decision costs a single fast RPC (the ext_proc call) rather than adding an extra hop for each token in the response. But keep in mind what the EPP actually reads: instantaneous snapshots of each pod. The metrics look like this:
Those three numbers are point-in-time signals: queue depth now, running work now, and cache usage now. A pod that looks ideal at this instant may soon be occupied by a long-running job that arrived a millisecond earlier. The EPP cannot reliably foresee long requests from instantaneous metrics alone. So — what if the router could predict what’s about to happen, rather than only seeing what’s happening right now?
A dark-themed infographic titled "What It Can't See" showing "40,000 tokens" and a small "you" marker alongside two pods (pod1 at 30% and pod0 at 80%) with progress bars, a central "EPP" block, and a crossed-out eye icon.
Next steps usually explore adding prediction signals (request size estimates, model hints, historical patterns) or lightweight admission control to improve routing accuracy. But the foundational idea remains: the EPP combines a Kubernetes API watch and a per-pod metrics scrape to form a live, low-latency routing brain. Summary table References

Watch Video