CRDs
InferenceWorkerSet
An InferenceWorkerSet represents a set of local inference workers (e.g. vLLM pods) that serve one model. An InferenceWorkerSet wraps a standard Kubernetes Service with inference-specific routing information including model and KV cache information.
- Version:
kinference.buoyant.io/v1alpha1 - Short name:
iws
InferenceWorkerSets are typically used as part of an InferencePool.
InferenceWorkerSets can also specify aliases for model names.
Example #
This InferenceWorkerSet wraps a Service named qwen-3-6-35b-a3b-vllm; specifies
that it has a KV cache block size 16 and emits KV cache events on the port named
“kv-pub”, and adds “qwen3.6” as an alias to the “Qwen/Qwen3.6-35B-A3B” model
served.
apiVersion: kinference.buoyant.io/v1alpha1
kind: InferenceWorkerSet
metadata:
name: qwen-3-6-35b-a3b
namespace: default
spec:
profile: default
kvBlockSize: 16
serviceRef:
name: qwen-3-6-35b-a3b-vllm
port: http
kvEvents:
port: kv-pub
models:
- name: Qwen/Qwen3.6-35B-A3B
aliases: [qwen3.6]Specification #
| Field | Type | Required | Default |
|---|---|---|---|
profile | string | yes | — |
kvBlockSize | integer | yes | — |
serviceRef | object | yes | — |
serviceRef.name | string | yes | — |
serviceRef.port | string or integer | yes | — |
kvEvents.port | string or integer | yes, if kvEvents is set | — |
kvEvents.replayPort | string or integer | no | port named kv-replay, else replay disabled |
metricsPort | string or integer | no | port named metrics, else serviceRef.port |
modelDiscovery.mode | Endpoint | Static | no | Endpoint |
modelDiscovery.interval | duration | no | 30s |
models | list | no | — |
models[].name | string | yes | — |
models[].aliases | list | no | — |
profile field
#
Which routing profile to use. Determines the latency-model parameters and tokenization mode.
kvBlockSize field
#
Tokens per block in the KV-cache events these engines publish, e.g. 16, 256.
Note: This value is Not necessarily the same as the --block-size parameter
passed in to vLLM, or supplied in cache_config_info metrics. For example,
Gemma 4 reports block_size=64 in cache_config_info but publishes 256-token
blocks.
kvEvents field
#
The socket on which the workers publish KV cache events (BlockStored /
BlockRemoved and equivalents).
If unset, KV cache information will not be used. This may result in degraded routing quality.
models field
#
Model information is auto-discovered from /v1/models. This optional field is
used to provide model aliases. For example:
models:
- name: Qwen/Qwen2.5-32B-Instruct-AWQ # what the engine reports
aliases: [qwen-2.5, qwen-simple] # and two shorter ways to ask for itIn this example, users may call the Qwen2.5-32B-Instruct-AWQ model by passing the full model name or either of the two aliases (“qwen-2.5” or “qwen-simple”).
Status fields #
| Field | Meaning |
|---|---|
models[].upstreamName | Model name from the worker itself |
models[].aliases | Any aliases provided |
models[].readyEndpoints | Ready endpoints serving this model |
models[].contextWindow | Effective window (vLLM max_model_len) across ready endpoints, as {min, max} |
endpoints.ready | Candidate endpoints |
endpoints.terminating | Draining: still serving in-flight requests, no longer candidates |
kvEvents.configured | False when spec.kvEvents is unset |
kvEvents.subscribedEndpoints | Endpoints the router is currently receiving events from |
referencedBy | Pools listing this set in spec.destinations, sorted |
conditions | Accepted, Ready, Referenced |
| Condition | Question |
|---|---|
Accepted | Is the object valid and materialized as a routable pool? |
Ready | Does it have ready endpoints? |
Referenced | Does any InferencePool route to it — i.e. does pool-scoped policy apply? |
Printer columns and state #
Example:
$ kubectl get iws
NAME READY STATE AGE
qwen-3-6-35b-a3b 3 Accepted 7d
gemma-4-12b 0 Accepted,NotReady 7d
orphan 0 Accepted,NotReady,Unreferenced 19s
no-such-profile 0 UnknownProfile 2mREADY is the number of ready endpoints; STATE is a comma-separated list of
states:
| States | Position | Means |
|---|---|---|
Accepted | primary | Valid and materialized as a routable pool |
NotReady | qualifier | No ready endpoints |
Unreferenced | qualifier | No pool references this InferenceWorkerSet |
| <a rejection reason> | primary | Refused |
Possible rejection reasons include:
| Reason | spec is |
|---|---|
MissingProfile | missing profile |
UnknownProfile | naming a profile the router does not define |
MissingKvBlockSize / InvalidKvBlockSize | missing kvBlockSize, or giving one that is zero |
MissingServiceRef | missing serviceRef.name, so there is nothing to watch |
KvEventsMissingPort | declaring kvEvents with no port |
ModelMissingName | carrying a models row with no name |
Diagnoses #
| Symptom | Cause |
|---|---|
Accepted=False, UnknownProfile | spec.profile names an unknown profile not in Kinference’s configuration |
Accepted=False, KvEventsMissingPort | spec.kvEvents is present with no port. Omit the block, or name the port |
Accepted=False, ModelMissingName | A spec.models row has no name. |
Accepted=False, MissingProfile / MissingServiceRef | The field is absent. Only reachable by an object that bypassed admission |
STATE blank in kubectl get iws | No status written at all–the InferenceWorkerSet is not in a namespace that Kinference is watching |
STATE has Unreferenced | No pool references this InferenceWorkerSet. (Not an error.) |
Ready=False, NoReadyEndpoints | The Service has no ready endpoints |
status.endpoints.ready: 0 | serviceRef.name matches no Service, or it has none ready. Check kubectl get endpointslices -l kubernetes.io/service-name=<name> |
Events arrive, index.blocks stays 0 | kvBlockSize mismatch, stage 1. Grep the logs for the published size |
Set drains after a few BlockStored | kvBlockSize mismatch, stage 2 |
| Endpoints added but unresponsive | Pod IPs unreachable from the router pod. Check NetworkPolicies |
| A written alias never resolves | Check it against status.models[].aliases, and confirm the request named a pool |