vLLM Production Stack helm chart

July 24, 2026 · View on GitHub

This helm chart lets users deploy multiple serving engines and a router into the Kubernetes cluster.

Key features

  • Support running multiple serving engines with multiple different models
  • Load the model weights directly from the existing PersistentVolumes

Prerequisites

  1. A running Kubernetes cluster with GPU. (You can set it up through minikube: https://minikube.sigs.k8s.io/docs/tutorials/nvidia/)
  2. Helm

Install the helm chart

helm dependency build
helm install llmstack . -f values-example.yaml

Uninstall the chart

helm uninstall llmstack

Configure the deployments

See helm/values.yaml for mode details.

Production Stack Helm Chart Values Reference

This table documents all available configuration values for the Production Stack Helm chart.

Table of Contents

Serving Engine Configuration

FieldTypeDefaultDescription
servingEngineSpec.enableEnginebooleantrueWhether to enable the serving engine deployment
servingEngineSpec.labelsmap{environment: "test", release: "test"}Customized labels for the serving engine deployment
servingEngineSpec.vllmApiKeystring/mapnull(Optional) API key for securing vLLM models. Can be a direct string or an object referencing an existing secret
servingEngineSpec.modelSpeclist[]Array of specifications for configuring multiple serving engine deployments running different models
servingEngineSpec.containerPortinteger8000Port the vLLM server container is listening on
servingEngineSpec.servicePortinteger80Port the service will listen on
servingEngineSpec.configsmap{}Set other environment variables from a config map
servingEngineSpec.strategymap{}Deployment strategy for the serving engine pods
servingEngineSpec.tolerationslist[]Tolerations configuration for the serving engine pods (when there are taints on nodes)
servingEngineSpec.runtimeClassNamestring"nvidia"RuntimeClassName configuration (set to "nvidia" if using GPU)
servingEngineSpec.schedulerNamestring""SchedulerName configuration for the serving engine pods
servingEngineSpec.securityContextmap{}Pod-level security context configuration for the serving engine pods
servingEngineSpec.containerSecurityContextmap{runAsNonRoot: false}Container-level security context configuration for the serving engine container
servingEngineSpec.extraPortslist[]List of additional ports to expose for the serving engine container
servingEngineSpec.startupProbe.initialDelaySecondsinteger15Number of seconds after container starts before startup probe is initiated
servingEngineSpec.startupProbe.periodSecondsinteger10How often (in seconds) to perform the startup probe
servingEngineSpec.startupProbe.failureThresholdinteger60Number of failures before considering failed
servingEngineSpec.startupProbe.httpGet.pathstring"/health"Path to access on the HTTP server
servingEngineSpec.startupProbe.httpGet.portinteger8000Port to access on the container
servingEngineSpec.livenessProbe.initialDelaySecondsinteger15Number of seconds after container starts before liveness probe is initiated
servingEngineSpec.livenessProbe.periodSecondsinteger10How often (in seconds) to perform the liveness probe
servingEngineSpec.livenessProbe.failureThresholdinteger3Number of failures before considering failed
servingEngineSpec.livenessProbe.httpGet.pathstring"/health"Path to access on the HTTP server
servingEngineSpec.livenessProbe.httpGet.portinteger8000Port to access on the container
servingEngineSpec.imagePullPolicystring"Always"Image pull policy for serving engine
servingEngineSpec.extraVolumeslist[]Extra volumes for serving engine
servingEngineSpec.extraVolumeMountslist[]Extra volume mounts for serving engine
servingEngineSpec.envlist[](Optional) Global environment variables for all serving engine containers. If a variable is set in both servingEngineSpec.env and servingEngineSpec.modelSpec[].env, the value from modelSpec[].env will override the global value for that model.

Model Specification Fields

FieldTypeDefaultDescription
servingEngineSpec.modelSpec[].annotationsmap{}(Optional) Annotations to add to the deployment, e.g., {model: "opt125m"}
servingEngineSpec.modelSpec[].podAnnotationsmap{}(Optional) Annotations to add to the pod, e.g., {model: "opt125m"}
servingEngineSpec.modelSpec[].labelsmap{}(Optional) Additional labels to add to the deployment
servingEngineSpec.modelSpec[].podLabelsmap{}(Optional) Additional labels to add to the pods
servingEngineSpec.modelSpec[].namestring""The name of the model, e.g., "example-model"
servingEngineSpec.modelSpec[].repositorystring""The repository of the model, e.g., "vllm/vllm-openai"
servingEngineSpec.modelSpec[].tagstring""The tag of the model, e.g., "latest"
servingEngineSpec.modelSpec[].imagePullSecretstring""(Optional) Name of secret with credentials to private container repository
servingEngineSpec.modelSpec[].modelURLstring""The URL of the model, e.g., "facebook/opt-125m"
servingEngineSpec.modelSpec[].chatTemplatestringnull(Optional) Chat template (Jinja2) specifying tokenizer configuration
servingEngineSpec.modelSpec[].replicaCountinteger1The number of replicas for the model
servingEngineSpec.modelSpec[].pdb.enabledbooleanfalseWhether to create a PodDisruptionBudget for the model
servingEngineSpec.modelSpec[].pdb.labelsmap{}Labels to add to the PodDisruptionBudget
servingEngineSpec.modelSpec[].pdb.annotationsmap{}Annotations to add to the PodDisruptionBudget
servingEngineSpec.modelSpec[].pdb.minAvailablestring""Number of pods that are available after eviction as number or percentage (eg.: 50%)
servingEngineSpec.modelSpec[].pdb.maxUnavailablestring""Number of pods that are unavailable after eviction as number or percentage (eg.: 50%).
servingEngineSpec.modelSpec[].resourcesobject{}Standard Kubernetes resources block (requests/limits). If specified, this takes priority over and ignores simplified resource fields (requestCPU, requestMemory, requestGPU, etc.)
servingEngineSpec.modelSpec[].requestCPUinteger0The number of CPUs requested for the model
servingEngineSpec.modelSpec[].requestMemorystring""The amount of memory requested for the model, e.g., "16Gi"
servingEngineSpec.modelSpec[].requestGPUinteger0The number of GPUs requested for the model
servingEngineSpec.modelSpec[].requestGPUTypestring"nvidia.com/gpu"(Optional) The type of GPU requested, e.g., "nvidia.com/mig-4g.71gb"
servingEngineSpec.modelSpec[].limitCPUstring""(Optional) The CPU limit for the model, e.g., "8"
servingEngineSpec.modelSpec[].limitMemorystring""(Optional) The memory limit for the model, e.g., "32Gi"
servingEngineSpec.modelSpec[].shmSizestring"20Gi"Size of the shared memory for the serving engine container (applied when tensor parallelism is enabled)
servingEngineSpec.modelSpec[].enableLoRAbooleantrue(Optional) Whether to enable LoRA
servingEngineSpec.modelSpec[].pvcStoragestring""(Optional) The amount of storage requested for the model, e.g., "50Gi"
servingEngineSpec.modelSpec[].pvcAccessModelist[](Optional) The access mode policy for the mounted volume, e.g., ["ReadWriteOnce"]
servingEngineSpec.modelSpec[].storageClassstring""(Optional) The storage class of the PVC
servingEngineSpec.modelSpec[].pvcMatchLabelsmap{}(Optional) The labels to match the PVC, e.g., {model: "opt125m"}
servingEngineSpec.modelSpec[].pvcLabelsmap{}(Optional) The labels to add to the PVC, e.g., {label_excluded_from_alerts: "true"}
servingEngineSpec.modelSpec[].pvcAnnotationsmap{}(Optional) The annotations to add to the PVC
servingEngineSpec.modelSpec[].extraVolumeslist[](Optional) Additional volumes to add to the pod, in Kubernetes volume format
servingEngineSpec.modelSpec[].extraVolumeMountslist[](Optional) Additional volume mounts to add to the container, in Kubernetes volumeMount format
servingEngineSpec.modelSpec[].serviceAccountNamestring""(Optional) The name of the service account to use for the deployment
servingEngineSpec.modelSpec[].priorityClassNamestring""Priority class name for the deployment
servingEngineSpec.modelSpec[].hf_tokenstring/map-(Optional) Hugging Face token configuration
servingEngineSpec.modelSpec[].envlist-(Optional) Environment variables for the container
servingEngineSpec.modelSpec[].nodeNamestring-(Optional) Direct node assignment
servingEngineSpec.modelSpec[].nodeSelectorTermslist-(Optional) Node selector terms
servingEngineSpec.modelSpec[].tolerationslist-(Optional) Per-model tolerations. Merged with or replaces servingEngineSpec.tolerations according to tolerationsPolicy
servingEngineSpec.modelSpec[].tolerationsPolicystring"append"(Optional) How per-model tolerations interact with global tolerations. "append" unions both lists; "override" replaces global tolerations entirely for this model

Init Container Configuration

FieldTypeDefaultDescription
servingEngineSpec.modelSpec[].initContainer.namestring""The name of the init container, e.g., "init"
servingEngineSpec.modelSpec[].initContainer.imagestring""The Docker image for the init container, e.g., "busybox:latest"
servingEngineSpec.modelSpec[].initContainer.commandlist[](Optional) The command to run in the init container, e.g., ["sh", "-c"]
servingEngineSpec.modelSpec[].initContainer.argslist[](Optional) Additional arguments to pass to the command, e.g., ["ls"]
servingEngineSpec.modelSpec[].initContainer.envlist[](Optional) List of environment variables to set in the container
servingEngineSpec.modelSpec[].initContainer.resourcesmap{}(Optional) The resource requests and limits for the container
servingEngineSpec.modelSpec[].initContainer.mountPvcStoragebooleanfalse(Optional) Whether to mount the model's volume

vLLM Configuration

FieldTypeDefaultDescription
servingEngineSpec.modelSpec[].vllmConfig.v0integer-Specify to 1 to use vLLM v0, otherwise vLLM v1
servingEngineSpec.modelSpec[].vllmConfig.enablePrefixCachingbooleanfalseEnable prefix caching
servingEngineSpec.modelSpec[].vllmConfig.enableChunkedPrefillbooleanfalseEnable chunked prefill
servingEngineSpec.modelSpec[].vllmConfig.maxModelLeninteger4096The maximum model length, e.g., 16384
servingEngineSpec.modelSpec[].vllmConfig.dtypestring"fp16"The data type, e.g., "bfloat16"
servingEngineSpec.modelSpec[].vllmConfig.tensorParallelSizeinteger1The degree of tensor parallelism, e.g., 2
servingEngineSpec.modelSpec[].vllmConfig.maxNumSeqsinteger256Maximum number of sequences to be processed in a single iteration
servingEngineSpec.modelSpec[].vllmConfig.maxLorasinteger0The maximum number of LoRA models to be loaded in a single batch
servingEngineSpec.modelSpec[].vllmConfig.gpuMemoryUtilizationnumber0.9The fraction of GPU memory to be used for the model executor (0-1)
servingEngineSpec.modelSpec[].vllmConfig.runnerstring""The runner type for the model, can be "auto" or "pooling"
servingEngineSpec.modelSpec[].vllmConfig.convertstring""The conversion type for the model, can be "token_embed", "embed", "token_classify", "classify", or "score"
servingEngineSpec.modelSpec[].vllmConfig.extraArgslist["--trust-remote-code"]Extra command line arguments to pass to vLLM

LMCache Configuration

FieldTypeDefaultDescription
servingEngineSpec.modelSpec[].lmcacheConfig.enabledbooleanfalseEnable LMCache
servingEngineSpec.modelSpec[].lmcacheConfig.cpuOffloadingBufferSizestring"4"The CPU offloading buffer size, e.g., "30"
servingEngineSpec.modelSpec[].lmcacheConfig.diskOffloadingBufferSizestring""The disk offloading buffer size, e.g., "10Gi"
servingEngineSpec.modelSpec[].lmcacheConfig.enableControllerbooleantrueEnable LMCache controller for KV-aware routing
servingEngineSpec.modelSpec[].lmcacheConfig.instanceIdstring"default1"Unique instance identifier for controller
servingEngineSpec.modelSpec[].lmcacheConfig.controllerPortstring"9000"Controller port for KV coordination
servingEngineSpec.modelSpec[].lmcacheConfig.workerPortinteger8001Worker port for cache communication
servingEngineSpec.modelSpec[].lmcacheConfig.kvRolestring-KV cache role (for disaggregated prefill) - "kv_producer" or "kv_consumer"
servingEngineSpec.modelSpec[].lmcacheConfig.enableNixlbooleantrueEnable NIXL protocol for KV transfer
servingEngineSpec.modelSpec[].lmcacheConfig.nixlRolestring-NIXL role for distributed caching - "sender" or "receiver"
servingEngineSpec.modelSpec[].lmcacheConfig.nixlPeerHoststring"decode-service"NIXL peer host for KV transfer
servingEngineSpec.modelSpec[].lmcacheConfig.nixlPeerPortstring"55555"NIXL peer port for KV transfer
servingEngineSpec.modelSpec[].lmcacheConfig.nixlBufferSizestring"1073741824"NIXL buffer size for KV transfer
servingEngineSpec.modelSpec[].lmcacheConfig.logLevelstring"info"Log level for LMCache

KEDA Autoscaling Configuration

Note: Unless explicitly set, KEDA's default values will apply. The defaults shown below are KEDA's defaults, not values enforced by this Helm chart.

FieldTypeKEDA DefaultDescription
servingEngineSpec.modelSpec[].keda.enabledbooleanfalseEnable KEDA autoscaling for this model deployment (requires KEDA installed in cluster)
servingEngineSpec.modelSpec[].keda.minReplicaCountinteger-Minimum number of replicas (supports 0 for scale-to-zero); if not set, HPA minReplicas default applies
servingEngineSpec.modelSpec[].keda.maxReplicaCountinteger-Maximum number of replicas; if not set, HPA maxReplicas default applies
servingEngineSpec.modelSpec[].keda.pollingIntervalinteger30How often KEDA checks metrics (in seconds)
servingEngineSpec.modelSpec[].keda.cooldownPeriodinteger300Wait time before scaling down after scaling up (in seconds)
servingEngineSpec.modelSpec[].keda.idleReplicaCountinteger-Number of replicas when no triggers are active
servingEngineSpec.modelSpec[].keda.initialCooldownPeriodinteger-Initial cooldown period before scaling down after creation (in seconds)
servingEngineSpec.modelSpec[].keda.fallbackmap-Fallback configuration when scaler fails
servingEngineSpec.modelSpec[].keda.fallback.failureThresholdinteger-Number of consecutive failures before fallback
servingEngineSpec.modelSpec[].keda.fallback.replicasinteger-Number of replicas to scale to in fallback
servingEngineSpec.modelSpec[].keda.triggerslistSee belowList of KEDA trigger configurations (Prometheus-based)
servingEngineSpec.modelSpec[].keda.triggers[].typestring-Trigger type (e.g., "prometheus")
servingEngineSpec.modelSpec[].keda.triggers[].metadata.serverAddressstring-Prometheus server URL (e.g., http://prometheus-operated.monitoring.svc:9090)
servingEngineSpec.modelSpec[].keda.triggers[].metadata.metricNamestring-Name of the metric to monitor
servingEngineSpec.modelSpec[].keda.triggers[].metadata.querystring-PromQL query to fetch the metric
servingEngineSpec.modelSpec[].keda.triggers[].metadata.thresholdstring-Threshold value that triggers scaling
servingEngineSpec.modelSpec[].keda.advancedmap-Advanced KEDA configuration options
servingEngineSpec.modelSpec[].keda.advanced.restoreToOriginalReplicaCountbooleanfalseRestore original replica count when ScaledObject is deleted
servingEngineSpec.modelSpec[].keda.advanced.horizontalPodAutoscalerConfigmap-HPA-specific configuration
servingEngineSpec.modelSpec[].keda.advanced.horizontalPodAutoscalerConfig.namestringkeda-hpa-{scaled-object-name}Custom name for HPA resource
servingEngineSpec.modelSpec[].keda.advanced.horizontalPodAutoscalerConfig.behaviormap-HPA scaling behavior configuration (see K8s docs)
servingEngineSpec.modelSpec[].keda.advanced.scalingModifiersmap-Scaling modifiers for composite metrics
servingEngineSpec.modelSpec[].keda.advanced.scalingModifiers.targetstring-Target value for the composed metric
servingEngineSpec.modelSpec[].keda.advanced.scalingModifiers.activationTargetstring-Activation target for the composed metric
servingEngineSpec.modelSpec[].keda.advanced.scalingModifiers.metricTypestring"AverageValue"Metric type (AverageValue or Value)
servingEngineSpec.modelSpec[].keda.advanced.scalingModifiers.formulastring-Formula to compose metrics together

Ray Cluster Configuration

Set servingEngineSpec.modelSpec[].raySpec.enabled: true to deploy the model as a multi-node RayCluster (via KubeRay) instead of a standard Deployment. Worker resources and the worker init container are taken from the top-level modelSpec (requestCPU/requestMemory/requestGPU or resources, and initContainer), while head-node resources and the head-node init container are configured under raySpec.headNode.

FieldTypeDefaultDescription
servingEngineSpec.modelSpec[].raySpec.enabledbooleanfalseDeploy the model as a RayCluster instead of a Deployment
servingEngineSpec.modelSpec[].raySpec.headNode.requestCPUinteger1CPU request for the Ray head node container
servingEngineSpec.modelSpec[].raySpec.headNode.requestMemorystring"1Gi"Memory request for the Ray head node container
servingEngineSpec.modelSpec[].raySpec.headNode.requestGPUinteger1GPU request for the Ray head node container
servingEngineSpec.modelSpec[].raySpec.headNode.resourcesmap{}(Optional) Raw Kubernetes resources block for the head container. When set, overrides the request* fields above
servingEngineSpec.modelSpec[].raySpec.headNode.initContainer.namestring-(Optional) Name of an init container to run before the Ray head container
servingEngineSpec.modelSpec[].raySpec.headNode.initContainer.imagestring-(Optional) Image for the head init container
servingEngineSpec.modelSpec[].raySpec.headNode.initContainer.commandlist[](Optional) Command for the head init container
servingEngineSpec.modelSpec[].raySpec.headNode.initContainer.argslist[](Optional) Args for the head init container
servingEngineSpec.modelSpec[].raySpec.headNode.initContainer.envlist[](Optional) Environment variables for the head init container
servingEngineSpec.modelSpec[].raySpec.headNode.initContainer.resourcesmap{}(Optional) Resource requests/limits for the head init container
servingEngineSpec.modelSpec[].raySpec.headNode.initContainer.mountPvcStoragebooleanfalse(Optional) Mount the model's PVC into the head init container
servingEngineSpec.modelSpec[].raySpec.headNode.initContainer.extraVolumeMountslist[](Optional) Additional volume mounts for the head init container

Note: Ray worker pods reuse the top-level modelSpec fields — set modelSpec.requestCPU/requestMemory/requestGPU (or modelSpec.resources for a raw override) to size the workers, and modelSpec.initContainer to inject an init container into each worker pod.

Serving Engine Monitoring Configuration

FieldTypeDefaultDescription
servingEngineSpec.serviceMonitor.enabledbooleanfalseSpecifies whether to create a ServiceMonitor resource for collecting Prometheus metrics
servingEngineSpec.serviceMonitor.additionalLabelsmap{}Additional labels
servingEngineSpec.serviceMonitor.intervalstring30sInterval to scrape metrics
servingEngineSpec.serviceMonitor.scrapeTimeoutstring25sTimeout if metrics can't be retrieved in given time interval
servingEngineSpec.serviceMonitor.honorLabelsbooleanfalseLet prometheus add an exported_ prefix to conflicting labels
servingEngineSpec.serviceMonitor.metricRelabelingslist[]Metric relabel configs to apply to samples before ingestion. Metric Relabeling
servingEngineSpec.serviceMonitor.relabelingslist[]Relabel configs to apply to samples before ingestion. Relabeling

Router Configuration

FieldTypeDefaultDescription
routerSpec.enableRouterbooleantrueWhether to enable the router service
routerSpec.repositorystring"lmcache/lmstack-router"Docker image repository for the router
routerSpec.tagstring"latest"Docker image tag for the router
routerSpec.imagePullPolicystring"Always"Image pull policy for the router
routerSpec.imagePullSecretslist[]Image pull secrets for private container registries
routerSpec.replicaCountinteger1Number of replicas for the router pod
routerSpec.pdb.enabledbooleanfalseWhether to create a PodDisruptionBudget for the model
routerSpec.pdb.labelsmap{}Labels to add to the PodDisruptionBudget
routerSpec.pdb.annotationsmap{}Annotations to add to the PodDisruptionBudget
routerSpec.pdb.minAvailablestring""Number of pods that are available after eviction as number or percentage (eg.: 50%)
routerSpec.pdb.maxUnavailablestring""Number of pods that are unavailable after eviction as number or percentage (eg.: 50%).
routerSpec.priorityClassNamestring""Priority class for router
routerSpec.containerPortinteger8000Port the router container is listening on
routerSpec.serviceTypestring"ClusterIP"Kubernetes service type for the router
routerSpec.serviceAnnotationsmap{}Service annotations for LoadBalancer/NodePort
routerSpec.servicePortinteger80Port the router service will listen on
routerSpec.serviceDiscoverystring"k8s"Service discovery mode ("k8s" or "static")
routerSpec.k8sServiceDiscoveryTypestring"pod-ip"Service discovery Type ("pod-ip" or "service-name") if serviceDiscovery is "k8s"
routerSpec.staticBackendsstring""Comma-separated list of backend addresses if serviceDiscovery is "static"
routerSpec.staticModelsstring""Comma-separated list of model names if serviceDiscovery is "static"
routerSpec.routingLogicstring"roundrobin"Routing logic: "roundrobin", "session", "prefixaware", "kvaware", "disaggregated_prefill", or "disaggregated_prefill_orchestrated"
routerSpec.prefixMinMatchLengthinteger0Minimum prefix match length for prefixaware routing to reuse a matched endpoint; below this, requests fall back to QPS routing. Quantized to the prefix chunk size (default 128). 0 disables it
routerSpec.sessionKeystring""Session key if using "session" routing logic
routerSpec.extraArgslist[]Extra command line arguments to pass to the router
routerSpec.extraVolumeslist[]Additional volumes to add to the router pod, in Kubernetes volume format
routerSpec.extraVolumeMountslist[]Additional volume mounts to add to the router container, in Kubernetes volumeMount format
routerSpec.engineScrapeIntervalinteger15Interval in seconds to scrape metrics from the serving engine
routerSpec.requestStatsWindowinteger60Window size in seconds for calculating request statistics
routerSpec.strategymap{}Deployment strategy for the router pods
routerSpec.vllmApiKeystring/mapnull(Optional) API key for securing vLLM models
routerSpec.resources.requests.cpustring"4"CPU requests for router
routerSpec.resources.requests.memorystring"16G"Memory requests for router
routerSpec.resources.limits.cpustring"8"CPU limits for router
routerSpec.resources.limits.memorystring"32G"Memory limits for router
routerSpec.labelsmap{environment: "router", release: "router"}Customized labels for the router deployment
routerSpec.podAnnotationsmap{}(Optional) Annotations to add to the pod, e.g., {model: "opt125m"}
routerSpec.affinitymap{}(Optional) Affinity configuration. If specified, this takes precedence over nodeSelectorTerms.
routerSpec.nodeSelectorTermslist[](Optional) Node selector terms. This is ignored if affinity is specified.
routerSpec.hf_tokenstring""Hugging Face token for router
routerSpec.lmcacheControllerPortinteger""LMCache controller port, used when routingLogic is "kvaware" (e.g. 9000)
routerSpec.lmcacheConfig.logLevelstring"INFO"Log level for LMCache in the router when routingLogic is kvaware
routerSpec.livenessProbe.initialDelaySecondsinteger30Initial delay in seconds for router's liveness probe
routerSpec.livenessProbe.periodSecondsinteger5Interval in seconds for router's liveness probe
routerSpec.livenessProbe.failureThresholdinteger3Failure threshold for router's liveness probe
routerSpec.livenessProbe.httpGet.pathstring"/health"Endpoint that the router's liveness probe will be testing
routerSpec.startupProbe.initialDelaySecondsinteger5Initial delay in seconds for router's startup probe
routerSpec.startupProbe.periodSecondsinteger5Interval in seconds for router's startup probe
routerSpec.startupProbe.failureThresholdinteger3Failure threshold for router's startup probe
routerSpec.startupProbe.httpGet.pathstring"/health"Endpoint that the router's startup probe will be testing
routerSpec.readinessProbe.initialDelaySecondsinteger30Initial delay in seconds for router's readiness probe
routerSpec.readinessProbe.periodSecondsinteger5Interval in seconds for router's readiness probe
routerSpec.readinessProbe.failureThresholdinteger3Failure threshold for router's readiness probe
routerSpec.readinessProbe.httpGet.pathstring"/health"Endpoint that the router's readiness probe will be testing

Router OpenTelemetry Configuration

FieldTypeDefaultDescription
routerSpec.otel.endpointstring""OTLP endpoint for tracing (e.g., "otel-collector:4317"). Tracing is enabled when this is set.
routerSpec.otel.serviceNamestring"vllm-router"Service name for OpenTelemetry traces
routerSpec.otel.securebooleanfalseUse secure (TLS) connection for OTLP exporter

Router Monitoring Configuration

FieldTypeDefaultDescription
routerSpec.serviceMonitor.enabledbooleanfalseSpecifies whether to create a ServiceMonitor resource for collecting Prometheus metrics
routerSpec.serviceMonitor.additionalLabelsmap{}Additional labels
routerSpec.serviceMonitor.intervalstring30sInterval to scrape metrics
routerSpec.serviceMonitor.scrapeTimeoutstring25sTimeout if metrics can't be retrieved in given time interval
routerSpec.serviceMonitor.honorLabelsbooleanfalseLet prometheus add an exported_ prefix to conflicting labels
routerSpec.serviceMonitor.metricRelabelingslist[]Metric relabel configs to apply to samples before ingestion. Metric Relabeling
routerSpec.serviceMonitor.relabelingslist[]Relabel configs to apply to samples before ingestion. Relabeling

Router Ingress Configuration

FieldTypeDefaultDescription
routerSpec.ingress.enabledbooleanfalseEnable Ingress controller resource for the router
routerSpec.ingress.classNamestring""IngressClass to use for the router Ingress resource
routerSpec.ingress.annotationsmap{}Additional annotations for the router Ingress resource
routerSpec.ingress.hostslist[{host: "vllm-router.local", paths: [{path: /, pathType: Prefix}]}]List of hostnames covered by the router Ingress record
routerSpec.ingress.tlslist[]TLS configuration for hostnames covered by the router Ingress record

Cache Server Configuration

FieldTypeDefaultDescription
cacheserverSpec.enabledbooleanfalseWhether to enable the cache server deployment
cacheserverSpec.image.repositorystring"lmcache/vllm-openai"Docker image repository for the cache server
cacheserverSpec.image.tagstring"latest"Docker image tag for the cache server
cacheserverSpec.imagePullPolicystring"Always"Image pull policy for the cache server
cacheserverSpec.imagePullSecretslist[]Image pull secrets for private container registries
cacheserverSpec.replicaCountinteger1Number of replicas for the cache server pod
cacheserverSpec.containerPortinteger8000Port the cache server container is listening on
cacheserverSpec.serviceTypestring"ClusterIP"Kubernetes service type for the cache server
cacheserverSpec.servicePortinteger80Port the cache server service will listen on
cacheserverSpec.resourcesmap{}Resource requests and limits
cacheserverSpec.labelsmap{environment: "cache", release: "cache"}Customized labels for the cache server deployment
cacheserverSpec.strategymap{}Deployment strategy for the cache server pods
cacheserverSpec.livenessProbemap{initialDelaySeconds: 15, periodSeconds: 10, failureThreshold: 3, httpGet: {path: /health, port: 8000}}Configuration for the liveness probe
cacheserverSpec.tolerationslist[]Tolerations configuration for the cache server pods
cacheserverSpec.runtimeClassNamestring""RuntimeClassName configuration for the cache server pods
cacheserverSpec.schedulerNamestring""SchedulerName configuration for the cache server pods
cacheserverSpec.securityContextmap{}Pod-level security context configuration
cacheserverSpec.containerSecurityContextmap{}Container-level security context configuration
cacheserverSpec.priorityClassNamestring-Priority class for cache server
cacheserverSpec.affinitymap-(Optional) Affinity configuration. If specified, this takes precedence over nodeSelectorTerms.
cacheserverSpec.nodeSelectormap-(Optional) nodeSelector for the cache pods.
cacheserverSpec.serdestring-Serialization/deserialization format

cacheserverSpec.resources is passed through directly to the pod container resources block, so you can use extended resource keys (for example rdma/ib) in addition to cpu/memory.

LoRA Adapters Configuration

FieldTypeDefaultDescription
loraAdapterslist[]Array of LoRA adapter instances to deploy
loraAdapters[].namestring-Name of the LoRA adapter instance
loraAdapters[].baseModelstring-Name of the base model this adapter is for
loraAdapters[].vllmApiKey.secretRef.secretNamestring-Name of the secret containing API key
loraAdapters[].vllmApiKey.secretRef.secretKeystring-Key in the secret containing API key
loraAdapters[].vllmApiKey.valuestring-Direct API key value
loraAdapters[].adapterSource.typestring-Type of adapter source (local, s3, http, huggingface)
loraAdapters[].adapterSource.adapterNamestring-Name of the adapter to apply
loraAdapters[].adapterSource.adapterPathstring-Path to the LoRA adapter weights
loraAdapters[].adapterSource.repositorystring-Repository to get the LoRA adapter from
loraAdapters[].adapterSource.patternstring-Pattern to use for the adapter name
loraAdapters[].adapterSource.maxAdaptersinteger-Maximum number of adapters to load
loraAdapters[].adapterSource.credentials.secretNamestring-Name of secret with storage credentials
loraAdapters[].adapterSource.credentials.secretKeystring-Key in secret containing credentials
loraAdapters[].loraAdapterDeploymentConfig.algorithmstring-Placement algorithm (default, ordered, equalized)
loraAdapters[].loraAdapterDeploymentConfig.replicasinteger-Number of replicas that should load this adapter
loraAdapters[].labelsmap-Additional labels for the LoRA adapter

LoRA Controller Configuration

FieldTypeDefaultDescription
loraController.enableLoraControllerbooleanfalseWhether to enable the LoRA controller
loraController.kubernetesClusterDomainstring"cluster.local"Kubernetes cluster domain
loraController.replicaCountinteger1Number of LoRA controller replicas
loraController.image.repositorystring"lmcache/lmstack-lora-controller"Docker image repository
loraController.image.tagstring"latest"Docker image tag
loraController.image.pullPolicystring"IfNotPresent"Image pull policy
loraController.imagePullSecretslist[]Image pull secrets
loraController.annotationsmap{}Deployment annotations
loraController.labelsmap{}Deployment labels
loraController.podAnnotationsmap{}Pod annotations
loraController.podLabelsmap{}Pod labels
loraController.podSecurityContext.runAsNonRootbooleantrueRun as non-root user
loraController.podSecurityContext.seccompProfile.typestringRuntimeDefaultSeccomp profile type
loraController.containerSecurityContext.allowPrivilegeEscalationbooleanfalseAllow privilege escalation
loraController.containerSecurityContext.capabilities.droplist["ALL"]Drop capabilities
loraController.resourcesmap{}Resource requests and limits
loraController.nodeSelectormap{}Node selector
loraController.affinitymap{}Affinity configuration
loraController.tolerationslist[]Tolerations configuration
loraController.envlist[]Environment variables
loraController.extraArgslist[]Extra arguments for the controller
loraController.metrics.enabledbooleantrueWhether to expose lora controller metrics
loraController.pdb.enabledbooleanfalseWhether to create a PodDisruptionBudget for the loraController
loraController.pdb.labelsmap{}Labels to add to the PodDisruptionBudget
loraController.pdb.annotationsmap{}Annotations to add to the PodDisruptionBudget
loraController.pdb.minAvailablestring""Number of pods that are available after eviction as number or percentage (eg.: 50%)
loraController.pdb.maxUnavailablestring""Number of pods that are unavailable after eviction as number or percentage (eg.: 50%).

Shared Storage Configuration

FieldTypeDefaultDescription
sharedPvcStorage.enabledbooleanfalseWhether to enable shared storage for the models
sharedPvcStorage.sizestring"100Gi"Size of the shared storage volume
sharedPvcStorage.accessModeslist["ReadWriteMany"]Access modes for the shared storage volume
sharedPvcStorage.storageClassstring""Storage class name for the shared storage volume
sharedPvcStorage.hostPathstring""Host path for the shared storage volume. Specifying a hostPath or nfs server will create a PersistentVolume. These fields should be omitted to rely on dynamic provisioning of PersistentVolumeClaims.
sharedPvcStorage.nfs.serverstring""NFS server address for the shared storage volume
sharedPvcStorage.nfs.pathstring""NFS export path for the shared storage volume

Other Configuration

FieldTypeDefaultDescription
grafanaDashboards.enabledbooleanfalseWhether to deploy grafana dashboards as configmaps.
grafanaDashboards.annotationsmap{}Annotations to add to the configmaps.
grafanaDashboards.labelsmap{grafana_dashboard: "1"}Labels for the configmaps
extraObjectslist[]Array of extra Kubernetes objects to deploy. Each object should be a valid Kubernetes manifest in YAML format. This can be used to deploy additional resources such as ConfigMaps, Secrets, or custom resources that are not directly supported by the chart's built-in configuration. Supports use of custom Helm templates.

Observability

Grafana dashboard to monitor the deployment

Deploy the observability stack

On a cluster with prometheus operator installed

Install the chart with the following helm values to create the serviceMonitor and grafana dashboards.

servingEngineSpec:
  serviceMonitor:
    enabled: true
routerSpec:
  serviceMonitor:
    enabled: true
grafanaDashboards:
  enabled: true

On an empty cluster

The vllm-stack chart embeds kube-prometheus-stack as a subchart. Install the chart with the following helm values to deploy prometheus and grafana

servingEngineSpec:
  serviceMonitor:
    enabled: true
routerSpec:
  serviceMonitor:
    enabled: true
grafanaDashboards:
  enabled: true

kube-prometheus-stack:
  enabled: true

Access the Grafana UI

Forward the Grafana dashboard port to the local node-port

kubectl port-forward svc/<release-name>-grafana 8080:80

Open the webpage at http://<IP of your node>:8080 to access the Grafana web page. The default user name is admin and the password can be configured in the values (default is generated by helm and stored in a secret <release-name>-grafana).

LMCache Dashboard

If you use the LMCache image in the production stack, the chart includes a dedicated LMCache dashboard (provisioned automatically alongside the main vLLM dashboard when grafanaDashboards.enabled is set to true). It contains six fields showing the benefits of CPU offloading: Average time to first token (sec), Cache hit rate (%) in last 1 minute, LMCache retrieve speed (K Tokens / sec), Local CPU cache usage (GB), Number of requested tokens in total, and Number of hit tokens in total.

Use Prometheus Adapter to export vLLM metrics

The vLLM router can export metrics to Prometheus using the Prometheus Adapter.

Install the chart with the following helm values to deploy prometheus-adapter

prometheus-adapter:
  enabled: true

We provide a minimal example of how to use the Prometheus Adapter to export vLLM metrics. See values.yaml for more details.

The exported metrics can be used for different purposes, such as horizontal scaling of the vLLM deployments.

To verify the metrics are being exported, you can use the following command:

kubectl get --raw /apis/custom.metrics.k8s.io/v1beta1 | jq | grep vllm_num_requests_waiting -C 10

You should see something like the following:

    {
      "name": "namespaces/vllm_num_requests_waiting",
      "singularName": "",
      "namespaced": false,
      "kind": "MetricValueList",
      "verbs": [
        "get"
      ]
    }

The following command will show the current value of the metric:

kubectl get --raw /apis/custom.metrics.k8s.io/v1beta1/namespaces/default/metrics/vllm_num_requests_waiting | jq

The output should look like the following:

{
  "kind": "MetricValueList",
  "apiVersion": "custom.metrics.k8s.io/v1beta1",
  "metadata": {},
  "items": [
    {
      "describedObject": {
        "kind": "Namespace",
        "name": "default",
        "apiVersion": "/v1"
      },
      "metricName": "vllm_num_requests_waiting",
      "timestamp": "2025-03-02T01:56:01Z",
      "value": "0",
      "selector": null
    }
  ]
}