Configuration

June 28, 2026 · View on GitHub

Table of Contents


Overview

The Inference Payload Processor (IPP) is configured through three layers:

  • Command-line arguments — Process-level settings: ports, logging verbosity, tracing, and the path to the config file. See Command-Line Arguments.
  • A YAML config file — The PayloadProcessorConfig, which declares the plugin pipeline: every plugin instance and how it is composed into profiles and the pre/post stages. This is the heart of IPP's behavior. See The PayloadProcessorConfig API.
  • ConfigMaps — Consumed by certain plugins at runtime. The base-model-to-header plugin watches labeled ConfigMaps that map model names (base models and LoRA adapters) to base models. See Model Mapping ConfigMaps.

In a Helm deployment, the config file is rendered into a ConfigMap and mounted into the IPP container; CLI flags are passed through Helm values. See Deployment (Helm).


The PayloadProcessorConfig API

The PayloadProcessorConfig is a YAML document that declares the entire plugin pipeline. The first two lines are constant and must appear as written:

apiVersion: llm-d.ai/v1alpha1
kind: PayloadProcessorConfig

All plugins are instantiated once under a top-level plugins list and then referenced by name from profiles and the pre/post stages. This mirrors the llm-d Router's EndpointPickerConfig model — the same plugin type can be instantiated multiple times under different names.

Note

IPP does not use a real CRD. The config is read with Kubernetes machinery, but no JSON-Schema validation is enforced at admission time; the Kubernetes validation markers in the API types are documentation only. Validation happens in the loader at startup.

Top-Level Fields

FieldRequiredTypeDescription
pluginsYes[]PluginSpecThe plugin instances to create. Every reference elsewhere resolves to a name declared here.
preProcessingNoPluginRefListOrdered references intended to run for every request before a profile is selected. Reserved: accepted by the config but not yet invoked by the request path.
profilePickerNoPluginRefThe plugin that chooses which profile to run. When exactly one profile is defined and no picker is set, the built-in single-profile-picker is enabled automatically.
profilesYes (min 1)[]ProfileThe named profiles. Exactly one runs per request.
postProcessingNoPluginRefListOrdered references intended to run for every request after the selected profile's response plugins. Reserved: accepted by the config but not yet invoked by the request path.
datalayerNoDatalayerConfigData-layer plugin references: collectors, extractors, and datasources.

PluginSpec

Each entry in the top-level plugins list declares one plugin instance:

FieldRequiredTypeDescription
nameNostringName by which other entries reference this instance. Defaults to the value of type when omitted.
typeYesstringThe plugin type to instantiate (e.g. body-field-to-header). See Plugins for available types.
parametersNoraw JSON/YAMLOpaque parameters passed to the plugin's factory function, which is responsible for parsing them. The schema varies per plugin.

PluginRef and PluginRefList

A PluginRef points at an instance declared in plugins:

FieldRequiredTypeDescription
pluginRefYesstringThe name of a plugin instance in the top-level plugins list.
weightFor scorersfloatWeight applied to a Scorer's contribution. Required when the referenced plugin is a Scorer (the loader rejects a scorer reference with no weight); ignored for non-scorer references.

A PluginRefList (used by preProcessing and postProcessing) is simply an object with a plugins list of PluginRef entries.

Profiles

A profile is a named set of request and response plugin references. Exactly one profile runs per request, chosen by the profile picker.

FieldRequiredTypeDescription
nameYesstringThe profile's name.
pluginsYesobjectHolds two ordered lists: request ([]PluginRef) and response ([]PluginRef).

A request entry may reference either a request-processor plugin or a model-selector plugin (Filter / Scorer / Picker); the config loader routes each reference to the correct extension point based on the interface the referenced plugin implements. Scorer references may carry a weight. See the Architecture doc for how the Filter → Score → Pick pipeline composes.

Data Layer

The optional datalayer section registers plugins that maintain cross-request state consumed by Filters and Scorers. It holds three PluginRef lists:

FieldTypeDescription
collectors[]PluginRefCollector plugins that aggregate signals over time.
extractors[]PluginRefExtractor plugins that pull metadata out of request/response events.
datasources[]PluginRefDataSource plugins that import external configuration into the store.

Annotated Example

A complete config that performs multi-pool routing (model-name extraction plus LoRA-to-base mapping) under a single auto-selected profile:

apiVersion: llm-d.ai/v1alpha1
kind: PayloadProcessorConfig

# Instantiate every plugin once. Each instance is addressable by `name`
# (which defaults to `type` when omitted).
plugins:
- type: body-field-to-header           # copy `model` from the body into a header
  parameters:
    fieldName: model
    headerName: X-Gateway-Model-Name
- type: base-model-to-header           # map model/adapter name to its base model
                                       # and inject X-Gateway-Base-Model-Name

# Optional: runs for every request before profile selection.
# preProcessing:
#   plugins:
#   - pluginRef: some-preprocessor

# Optional: when a single profile is defined, `single-profile-picker`
# is enabled automatically, so this can be omitted.
# profilePicker:
#   pluginRef: single-profile-picker

# At least one profile is required; exactly one runs per request.
profiles:
- name: default
  plugins:
    request:                           # ordered request-side pipeline
    - pluginRef: body-field-to-header
    - pluginRef: base-model-to-header
    response: []                       # no response-side processing

# Optional: runs for every request after the profile's response plugins.
# postProcessing:
#   plugins:
#   - pluginRef: some-postprocessor

# Optional: cross-request state for Filters/Scorers.
# datalayer:
#   collectors: []
#   extractors: []
#   datasources: []

A model-selection profile mixes request processors with model-selector plugins in the same request list and weights a scorer:

profiles:
- name: model-selection
  plugins:
    request:
    - pluginRef: model-selector            # entry point for Filter → Score → Pick
    - pluginRef: inflight-requests-scorer  # a Scorer
      weight: 1.0
    - pluginRef: max-score-picker          # the Picker
    response: []

Model Mapping ConfigMaps

Multi-pool routing requires IPP to know which base model each requested model name corresponds to. This mapping is supplied by Kubernetes ConfigMaps that the base-model-to-header plugin watches and loads at runtime — no IPP restart is needed when they change.

Structure

Each ConfigMap defines one base model and its LoRA adapters. To be watched by IPP, a ConfigMap must carry the label inference.llm-d.ai/ipp-managed: "true".

apiVersion: v1
kind: ConfigMap
metadata:
  name: qwen-model-mapping
  labels:
    inference.llm-d.ai/ipp-managed: "true"
data:
  baseModel: Qwen/Qwen2.5-1.5B-Instruct
  adapters: |
    - qwen-summarizer
    - qwen-classifier
Data fieldRequiredTypeDescription
baseModelYesstringThe base model name. Requests naming this model map to itself.
adaptersNoYAML listLoRA adapter names served by this base model. Each adapter name maps back to baseModel.

Mapping Rules

  • A requested model name that matches a baseModel maps to that base model.
  • A requested model name that matches an entry in an adapters list maps to that ConfigMap's baseModel.
  • The resolved base model is injected as the X-Gateway-Base-Model-Name header, which HTTPRoute rules match on to select the InferencePool. See HTTPRoute Configuration.
  • A name that matches nothing yields an empty base-model header; configure your HTTPRoute rules accordingly.

Important

Model names — both base models and adapters — must be globally unique across all pools. Because IPP resolves a name to exactly one base model, a name appearing in more than one ConfigMap (or reused as both a base model and an adapter) makes routing ambiguous. Keep one ConfigMap per base model and ensure no name collisions across them.

Multi-Model Example

Two base models, each in its own ConfigMap, with their adapters:

apiVersion: v1
kind: ConfigMap
metadata:
  name: qwen-model-mapping
  labels:
    inference.llm-d.ai/ipp-managed: "true"
data:
  baseModel: Qwen/Qwen2.5-1.5B-Instruct
  adapters: |
    - qwen-summarizer
    - qwen-classifier
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: deepseek-model-mapping
  labels:
    inference.llm-d.ai/ipp-managed: "true"
data:
  baseModel: deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B
  # adapters is optional; this base model serves no LoRA adapters.

With this mapping, a request for qwen-summarizer resolves to Qwen/Qwen2.5-1.5B-Instruct, while a request for deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B resolves to itself — each routed to its own pool by the HTTPRoute rules below.

Note

By default IPP watches only the namespace it is deployed in (the NAMESPACE env var). To watch ConfigMaps across namespaces, set the Helm value payloadProcessor.multiNamespace=true, which omits the NAMESPACE env var. See Environment Variables.


Deployment (Helm)

IPP ships a Helm chart that provisions the Deployment, Service, RBAC, the config ConfigMap, and the provider-specific proxy integration. The chart is deployed once per Gateway.

helm install payload-processor ./config/charts/payload-processor \
    --set provider.name=istio \
    --set inferenceGateway.name=inference-gateway

The full values table is documented in the chart README — see the Helm Chart reference. This section only highlights the values most relevant to configuration; it does not re-document every value.

Representative Values

payloadProcessor:
  name: payload-processor
  replicas: 1
  port: 9004              # ext_proc gRPC port
  healthCheckPort: 9005   # gRPC health/readiness port
  multiNamespace: false   # true → watch ConfigMaps across namespaces
  image:
    registry: ghcr.io/llm-d
    repository: llm-d-inference-payload-processor
    tag: main
    pullPolicy: IfNotPresent

  # CLI flags passed through to the binary as --<key>=<value>.
  flags:
    v: 3                  # log verbosity

  # Tracing (OTEL). When enabled, the chart injects OTEL_* env vars.
  tracing:
    enabled: false
    otelServiceName: "inference.llm-d.ai/inference-payload-processor"
    otelExporterEndpoint: "http://localhost:4317"
    sampling:
      sampler: "parentbased_traceidratio"
      samplerArg: "0.1"

provider:
  name: none              # istio | gke | none
  supportedEvents:
    requestHeaders: true
    requestBody: true
    requestTrailers: true
    responseHeaders: true
    responseBody: true
    responseTrailers: true

inferenceGateway:
  name: inference-gateway

Supplying a Custom Config

By default the chart mounts a built-in PayloadProcessorConfig (the shipped default lives in deploy/config/ipp-config.yaml). To supply your own pipeline, set payloadProcessor.customConfig to a PayloadProcessorConfig body; the chart renders it into the mounted ConfigMap and points --config-file at it.

payloadProcessor:
  customConfig:
    # The chart adds the apiVersion/kind header automatically — start at `plugins`.
    plugins:
    - type: body-field-to-header
      parameters:
        fieldName: model
        headerName: X-Gateway-Model-Name
    - type: base-model-to-header
    profiles:
    - name: default
      plugins:
        request:
        - pluginRef: body-field-to-header
        - pluginRef: base-model-to-header

Note

The custom config is the same PayloadProcessorConfig schema documented in The PayloadProcessorConfig API. Model-mapping ConfigMaps are applied separately from the chart, not embedded in customConfig.


Command-Line Arguments

IPP reads its process configuration from these flags (defined in pkg/server/options.go and the logging options). In Helm, set them via payloadProcessor.flags (which renders each as --<key>=<value>); --config-file, NAMESPACE, and the OTEL flags are wired by the chart.

FlagDefaultDescription
--config-file(empty)Path to the PayloadProcessorConfig YAML file.
--config-text(empty)The PayloadProcessorConfig provided inline as text, in lieu of a file.
--grpc-port9004gRPC port used for ext_proc communication with the proxy.
--grpc-health-port9005Port for gRPC liveness and readiness probes.
--metrics-port9090Port exposing the Prometheus /metrics endpoint.
--metrics-endpoint-authtrueEnable authentication and authorization on the metrics endpoint.
--secure-servingtrueServe the ext-proc gRPC endpoint over TLS (a self-signed certificate is generated at startup). Set to false for plaintext.
--tracingtrueEnable emitting OpenTelemetry traces.
--enable-pproftrueEnable pprof handlers. Set to false to disable.
-v, --v2Log verbosity level.
--zap-log-level(derived from -v)Zap log level. When unset, it is derived as -1 × v.

The logger also accepts the standard controller-runtime --zap-* flags (--zap-devel, --zap-encoder, --zap-stacktrace-level, --zap-time-encoding) for additional tuning.

Note

--config-file and --config-text are two ways to supply the config; if both are set, --config-text takes precedence. The three server ports (--grpc-port, --grpc-health-port, --metrics-port) are validated to be in 1–65535 and must all differ.


Environment Variables

VariableSet byDescription
NAMESPACEHelm (unless multiNamespace=true)Restricts the controller cache — and therefore ConfigMap watching — to this single namespace. When unset, IPP watches all namespaces.
OTEL_SERVICE_NAMEHelm (tracing)Service name reported on traces. Defaults to llm-d-ipp if unset.
OTEL_EXPORTER_OTLP_ENDPOINTHelm (tracing)OTLP collector endpoint. Defaults to http://localhost:4317 if unset.
OTEL_TRACES_EXPORTERHelm (tracing)Traces exporter (the chart sets otlp).
OTEL_TRACES_SAMPLERHelm (tracing)Sampler type (e.g. parentbased_traceidratio).
OTEL_TRACES_SAMPLER_ARGHelm (tracing)Sampler argument (e.g. 0.1).
OTEL_RESOURCE_ATTRIBUTESHelm (tracing)Resource attributes attached to traces (namespace, node, pod).

Note

The OTEL_* variables only take effect when tracing is enabled (--tracing=true, set via payloadProcessor.tracing.enabled in Helm). The chart also populates OTEL_RESOURCE_ATTRIBUTES from the pod's namespace, node, and pod name via the downward API.


Proxy Integration

IPP runs as an ext-proc service that the proxy invokes over Envoy's External Processing (ext-proc) protocol. The Helm chart provisions the provider-specific integration based on provider.name: istio generates an EnvoyFilter, gke generates a GCPRoutingExtension, and none provisions the core IPP resources (Deployment, Service, config, RBAC) but no proxy-integration resources — you wire the proxy integration yourself. In all cases the request body is streamed using ext-proc's FULL_DUPLEX_STREAMED body mode, which IPP requires to observe and mutate full bodies.

Istio (EnvoyFilter)

With provider.name=istio, the chart installs an EnvoyFilter that inserts the ext_proc filter into the target Gateway's HTTP filter chain (and a DestinationRule for the IPP service). The processing mode for each event reflects the provider.supportedEvents values — enabled events use SEND/ FULL_DUPLEX_STREAMED, disabled ones use SKIP/NONE:

apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: payload-processor
spec:
  targetRefs:
  - group: gateway.networking.k8s.io
    kind: Gateway
    name: inference-gateway
  configPatches:
  - applyTo: HTTP_FILTER
    match:
      context: GATEWAY
      listener:
        filterChain:
          filter:
            name: "envoy.filters.network.http_connection_manager"
    patch:
      operation: INSERT_FIRST
      value:
        name: envoy.filters.http.ext_proc.payload-processor
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.filters.http.ext_proc.v3.ExternalProcessor
          failure_mode_allow: false
          allow_mode_override: true
          processing_mode:
            request_header_mode: "SEND"
            response_header_mode: "SEND"
            request_body_mode: "FULL_DUPLEX_STREAMED"
            response_body_mode: "FULL_DUPLEX_STREAMED"
            request_trailer_mode: "SEND"
            response_trailer_mode: "SEND"
          grpc_service:
            envoy_grpc:
              cluster_name: outbound|9004||payload-processor.<namespace>.svc.cluster.local

The filter insertion point is controlled by provider.istio.envoyFilter.operation (default INSERT_FIRST) and provider.istio.envoyFilter.anchorSubFilter.

GKE (GCPRoutingExtension)

With provider.name=gke, the chart registers IPP as a routing extension via a GCPRoutingExtension (and a HealthCheckPolicy). The supportedEvents list and body send modes again follow provider.supportedEvents:

kind: GCPRoutingExtension
apiVersion: networking.gke.io/v1
metadata:
  name: payload-processor
spec:
  targetRefs:
  - group: "gateway.networking.k8s.io"
    kind: Gateway
    name: inference-gateway
  extensionChains:
  - name: chain1
    extensions:
    - name: ext1
      authority: "myext.com"
      timeout: 1s
      supportedEvents:
      - RequestHeaders
      - RequestBody
      - RequestTrailers
      - ResponseHeaders
      - ResponseBody
      - ResponseTrailers
      requestBodySendMode: "FullDuplexStreamed"
      responseBodySendMode: "FullDuplexStreamed"
      backendRef:
        group: ""
        kind: Service
        name: payload-processor
        port: 9004

HTTPRoute Configuration

IPP injects a routing header (the base-model-to-header plugin uses X-Gateway-Base-Model-Name); you then configure HTTPRoute resources that match on that header and route to the right InferencePool. These HTTPRoute resources are not created by the chart — they are part of your deployment.

A two-pool example routing on the injected base-model header:

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: qwen-route
spec:
  parentRefs:
  - name: inference-gateway
  rules:
  - matches:
    - headers:
      - type: Exact
        name: X-Gateway-Base-Model-Name
        value: Qwen/Qwen2.5-1.5B-Instruct
    backendRefs:
    - group: inference.networking.k8s.io
      kind: InferencePool
      name: qwen-pool
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: deepseek-route
spec:
  parentRefs:
  - name: inference-gateway
  rules:
  - matches:
    - headers:
      - type: Exact
        name: X-Gateway-Base-Model-Name
        value: deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B
    backendRefs:
    - group: inference.networking.k8s.io
      kind: InferencePool
      name: deepseek-pool

A request for the LoRA adapter qwen-summarizer is mapped to Qwen/Qwen2.5-1.5B-Instruct by IPP and matched by the first route; a request for the DeepSeek base model is matched by the second.

Tuning ext-proc Events

The six ext-proc events — requestHeaders, requestBody, requestTrailers, responseHeaders, responseBody, responseTrailers — are individually toggleable through provider.supportedEvents. Each enabled event is an extra network hop between the proxy and IPP, so enable only the events your configured plugins actually consume. For example, a routing-only deployment (which acts on the request body alone) can disable all response events:

provider:
  name: istio
  supportedEvents:
    requestHeaders: true
    requestBody: true
    requestTrailers: true
    responseHeaders: false
    responseBody: false
    responseTrailers: false

Monitoring

IPP exposes Prometheus metrics on an HTTP endpoint (default port 9090, path /metrics), configured via --metrics-port and --metrics-endpoint-auth. The full list of metrics — their names, types, labels, and intended use — is documented in Metrics. Tracing is handled separately via OpenTelemetry; see Environment Variables.


References

  • Architecture — How IPP works: ext-proc integration, the processing pipeline, profiles, model selection, and multi-pool routing.
  • Plugins — In-tree plugin reference and the pipeline configuration model.
  • Metrics — Prometheus metrics exposed by IPP.
  • Helm Chart — Chart install reference and the full values table.
  • llm-d — The end-to-end Multi-Model Routing guide lives in the llm-d repo.
  • External Processing (ext-proc) — The Envoy protocol IPP implements.