While services.xml defines the Vespa application specification, it abstracts away the underlying Kubernetes infrastructure.
Advanced users often need to configure Kubernetes-specific settings for Vespa Pods to integrate Vespa within their broader platform ecosystem.
The VespaSet supports three override targets — ConfigServer, profiles (container and content clusters), and ClusterControllers —
each with a typed merge policy that controls which fields may be added or modified.
All overrides accept a standard Kubernetes PodTemplateSpec.
The vespa container is the main container in every Pod type.
| Field | ConfigServer | Container | Content | ClusterControllers |
|---|---|---|---|---|
labels |
✅ | ✅ | ✅ | ✅ |
annotations |
✅ | ✅ | ✅ | ✅ |
volumes |
✅ | ✅ | ✅ | ✅ |
nodeSelector |
✅ | ✅ | ✅ | ✅ |
imagePullSecrets |
✅ | ✅ | ✅ | ✅ |
tolerations |
✅ | ✅ | ✅ | ✅ |
podAffinity |
✅ | ✅ | ✅ | ✅ |
podAntiAffinity |
✅ | ✅ | ✅ | ✅ |
nodeAffinity |
✅ | ✅ | ✅ | ✅ |
containers[vespa].env |
✅ | ✅ | ✅ | ✅ |
containers[vespa].volumeMounts |
✅ | ❌ | ❌ | ❌ |
containers[vespa].resources |
✅ | ❌ | ❌ | ❌ |
containers[] |
✅ | ✅ | ✅ | ✅ |
Any overlay field marked ❌ is explicitly rejected at admission time.
Overrides to the containers[vespa] are intentionally restricted to prevent corruption of operator-managed configuration.
On application Pods (container, content, ClusterControllers), setting these fields is rejected to protect the integrity
of the running Vespa process and prevent conflicts with services.xml.
ConfigServer overrides are defined under spec.configServer.podTemplate in the VespaSet resource.
The override policy explicitly allows adding volume mounts and overriding CPU and memory resources on the main vespa container.
A production Vespa cluster typically runs three ConfigServer Pods.
To guarantee that no two ConfigServer Pods share the same Kubernetes node — and therefore survive a single-node failure —
add a podAntiAffinity rule with topologyKey: kubernetes.io/hostname.
apiVersion: k8s.ai.vespa/v1
kind: VespaSet
metadata:
name: my-vespa-cluster
spec:
configServer:
podTemplate:
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
app.kubernetes.io/component: configserver
topologyKey: kubernetes.io/hostname
The VespaSet appends this anti-affinity term to the base template rather than replacing it, so any operator-managed affinity rules remain in effect.
Profile overrides allow different Pod configurations for different cluster types within the same VespaSet.
A profile maps a id as defined in services.xml to an PodTemplateSpec overlay. This
mapping can be set for an arbitrary number of Vespa Clusters.
A common pattern is to isolate each Vespa cluster type on its own node pool — container nodes for stateless query processing
and content nodes for storage and search. The following example uses nodeSelector in each profile.
In services.xml, declare a profile on each cluster:
<container id="default" version="1.0" profile="default"> <!-- ... --> </container> <content id="music" version="1.0" profile="music"> <!-- ... --> </content>
In the VespaSet, define a profile for each name:
apiVersion: k8s.ai.vespa/v1
kind: VespaSet
metadata:
name: my-vespa-cluster
spec:
profiles:
default:
podTemplate:
spec:
nodeSelector:
node-pool: vespa-container
music:
podTemplate:
spec:
nodeSelector:
node-pool: vespa-content
The VespaSet resolves the profile for each cluster at reconciliation time and overlays the corresponding
podTemplate onto the base PodSpec. Pods belonging to clusters with no matching profile entry receive no overlay.
clusterControllers is a reserved profile name. The VespaSet automatically applies this profile to all
cluster-controller Pods. If no clusterControllers profile is defined, cluster-controller Pods receive no overlay.
apiVersion: k8s.ai.vespa/v1
kind: VespaSet
metadata:
name: my-vespa-cluster
spec:
profiles:
clusterControllers:
podTemplate:
spec:
nodeSelector:
node-pool: vespa-admin
When a VespaSet is updated, the VespaSet evaluates the delta between the current and desired PodTemplateSpec
for each Pod and determines the minimum action required. Changes that do not affect scheduling — such as a label update — only
require the Pod to be recreated, leaving its Persistent Volume intact. Changes to scheduling constraints — such as
nodeSelector or affinity rules — require both the Pod and its Persistent Volume to be recreated, so that the Pod
can be placed on a new node and its data redistributed accordingly.
Redistributing data across content nodes after a Pod and Volume recreation may take time, depending on the size of the dataset.
When multiple fields change simultaneously, the most disruptive action takes precedence: if any change requires a Pod and Volume recreation, that action is applied regardless of whether other changes would have required only a Pod restart.
| Field | Recreate Pod | Recreate Pod and volume |
|---|---|---|
labels |
✅ | |
annotations |
✅ | |
nodeSelector |
✅ | |
nodeAffinity |
✅ | |
podAffinity |
✅ | |
podAntiAffinity |
✅ | |
tolerations |
✅ |
This set is representative. If a spec is not listed on the table, then automatic reconciliation will not take effect. In this case, you should manually delete the Pod and allow the Operator to recreate it