The Vespa Operator should be installed using the official Helm chart. It depends on the installation of the VespaSet Custom Resource Definition (CRD), which is defined at the Kubernetes cluster scope.
Through the Helm Chart, the installation of the CRD and the required RBAC permissions can be simplified. The required permissions are listed in the Permissions section.
Our container registry is located at images.ves.pa. For accessing the required Vespa on Kubernetes container images (and helm chart) you will need to contact us through our support portal.
We will provide you with the authentication id and token.
Important: For production use, we require mirroring these images into your own registry or a well-known internal repository appropriate for your infrastructure!
Our support team will provide you with credentials to access the following:
# The Vespa on Kubernetes Image export $OCI_IMAGE_REFERENCE
# The Vespa Operator Image export $OCI_IMAGE_REFERENCE_OPERATOR
# The Official Helm Chart export $HELM_OCI_CHART_REFERENCE
The following tools are encouraged for a smooth deployment.
These instructions assume that kubeconfig is pointing to an active Kubernetes cluster. Refer to the Getting Started guide to create a Kubernetes cluster. For instructions on creating a
local development deployment, refer to the MiniKube Setup section.
The Helm Chart installs the Vespa Operator, Role, RoleBinding, and ServiceAccount resources with the permissions to operate Vespa. Optionally, the
CRD specification can be installed onto the Kubernetes cluster.
An installation can be performed as follows. This will deploy the Vespa Operator to the target namespace and apply
the VespaSet CRD specification to the Kubernetes cluster. Set image.repository to the Vespa On Kubernetes Image provided by our support team.
The image.tag refers to the Vespa Version to deploy.
$ helm install vespa-operator $HELM_OCI_CHART_REFERENCE --namespace $NAMESPACE --create-namespace --set image.repository $OCI_IMAGE_REFERENCE_OPERATOR --set image.tag $OCI_IMAGE_TAG
If CRDs are managed separately, its installation can be disabled. However, the CRD specification must be manually applied to the Kubernetes cluster before installing the Helm Chart. Our support team can provide this specification if necessary.
$ kubectl apply vespasets.k8s.ai.vespa-v1.yaml $ helm install vespa-operator $HELM_OCI_CHART_REFERENCE --namespace $NAMESPACE --create-namespace --skip-crds --set image.repository $OCI_IMAGE_REFERENCE_OPERATOR --set image.tag $OCI_IMAGE_TAG
Ensure that the Deployment was successfully applied and the operator Pod was created. It can be done
using the following check.
$ kubectl wait --for=condition=available deployment/vespa-operator --timeout=120s -n $NAMESPACE \ && kubectl get pods -l app=vespa-operator -o wide -n $NAMESPACE
The full reference guide for the Helm Chart can be found in the Helm Reference section.
A VespaSet represents a quorum of ConfigServers that manage Vespa applications. Several examples of
VespaSet specifications are provided in the Helm Chart samples directory.
A sample VespaSet for Amazon Elastic Kubernetes Service (EKS) is shown below.
# VespaSet configuration for AWS EKS
apiVersion: k8s.ai.vespa/v1
kind: VespaSet
metadata:
name: vespaset-sample
namespace: $NAMESPACE
spec:
version: $OCI_IMAGE_TAG
configServer:
image: "$OCI_IMAGE_REFERENCE"
storageClass: "gp3"
generateRbac: false
application:
image: "$OCI_IMAGE_REFERENCE"
storageClass: "gp3"
ingress:
endpointType: "LOAD_BALANCER"
An example for a local deployment on MiniKube would be as follows.
# VespaSet configuration for MiniKube
apiVersion: k8s.ai.vespa/v1
kind: VespaSet
metadata:
name: vespaset-sample
namespace: $NAMESPACE
spec:
version: $OCI_IMAGE_TAG
configServer:
image: "$OCI_IMAGE_REFERENCE"
storageClass: "local-storage"
generateRbac: false
application:
image: "$OCI_IMAGE_REFERENCE"
storageClass: "local-storage"
ingress:
endpointType: "NONE"
Note that the $OCI_IMAGE_REFERENCE is shared between the ConfigServer and the Vespa Application Pods.
Apply the VespaSet to the Kubernetes Cluster. The operator will automatically detect the newly applied VespaSet and create a quorum of
ConfigServers.
The ConfigServers will then bootstrap the Vespa infrastructure. This process takes roughly a minute. The bootstrap process is completed once
the VespaSet shows the status as RUNNING for all ConfigServer Pods.
$ kubectl describe vespaset vespaset-sample -n $NAMESPACE
Name: vespaset-sample
Namespace: $NAMESPACE
Labels: <none>
Annotations: <none>
API Version: k8s.ai.vespa/v1
Kind: VespaSet
Metadata:
Creation Timestamp: 2026-01-29T21:32:27Z
Finalizers:
vespasets.k8s.ai.vespa/finalizer
Generation: 1
Resource Version: 121822902
UID: a70f56e9-6625-4011-acd7-9f7cad29dbc2
Spec:
Application:
Image: $OCI_IMAGE_REFERENCE
Storage Class: gp3
Config Server:
Generate Rbac: false
Image: $OCI_IMAGE_REFERENCE
Storage Class: gp3
Ingress:
Endpoint Type: LOAD_BALANCER
Version: $OCI_IMAGE_TAG
Status:
Bootstrap Status:
Pods:
cfg-0:
Last Updated: 2026-01-29T21:38:45Z
Message: Pod is running
Phase: RUNNING
Converged Version: $OCI_IMAGE_TAG
cfg-1:
Last Updated: 2026-01-29T21:38:09Z
Message: Pod is running
Phase: RUNNING
Converged Version: $OCI_IMAGE_TAG
cfg-2:
Last Updated: 2026-01-29T21:36:32Z
Message: Pod is running
Phase: RUNNING
Converged Version: $OCI_IMAGE_TAG
Last Transition Time: 2026-01-29T21:33:55Z
Message: All configservers running
Phase: RUNNING
Events: <none>
The status of the bootstrap process can be easily queried as follows.
$ kubectl get vespaset vespaset-sample -n $NAMESPACE -o json \ | jq -e ' .status.bootstrapStatus.pods as $p | ($p["cfg-0"].phase == "RUNNING") and ($p["cfg-1"].phase == "RUNNING") and ($p["cfg-2"].phase == "RUNNING") '
The full reference guide for the VespaSet can be found in the VespaSet Reference section.
A Vespa application can be deployed once the bootstrap process has completed. Refer to the Vespa Sample Applications to get started. In the following example, we will use the Album Recommendation sample.
Set up the Vespa CLI to download the Album Recommendation sample application to a working directory.
$ vespa clone album-recommendation myapp && cd myapp
Enable port-forwarding of the ConfigServer's ingress port to a local port.
$ vespa config set target local $ kubectl -n $NAMESPACE port-forward pod/cfg-0 19071:19071
Deploy the application.
$ vespa deploy --wait 600
The ConfigServer quorum will create the Container, Content, and Cluster-Controller Pods as specified in the application package. The deployment
is considered complete once all Pods show the phase RUNNING in the VespaSet status. This can be queried
as follows.
$ kubectl get vespaset vespaset-sample -n $NAMESPACE -o json \
| jq -e '
.status.bootstrapStatus.pods
| with_entries(
select(
.key as $k
| [
"cluster-controllers-104",
"cluster-controllers-105",
"cluster-controllers-106",
"default-100",
"default-101",
"documentation-102",
"documentation-103"
]
| index($k) | not
)
)
| all(.phase == "RUNNING")
'
The names of the Pods will change depending on your specific Application Package configuration.
Port-forwarding provides a simple way to access the ingress ports locally. For other ingress options, see the Configuring the External Access Layer section.
Feed documents to the Dataplane entrypoint by port-forwarding the Dataplane ingress port and the ConfigServer ingress port.
$ kubectl -n $NAMESPACE port-forward pod/cfg-0 19071:19071 $ kubectl -n $NAMESPACE port-forward pod/default-100 8080:8080
Then, use the Vespa CLI to feed a document.
vespa feed dataset/A-Head-Full-of-Dreams.json
The Vespa Operator requires the following permissions. These permissions are listed by Kubernetes API verbs per resource.
| Kubernetes Resource | Required Permissions |
|---|---|
| CustomResourceDefinitions | create, get, list, watch |
| VespaSet | get, list, watch, create, update, patch, delete |
| VespaSet Subresources |
vespasets/status: update, patchvespasets/finalizers: update
|
| ConfigMaps | get, list, watch, create, update, patch, delete |
| Services | get, list, watch, create, update, patch, delete |
| Pods | get, list, watch, create, update, patch, delete |
| Pod Execution | get, create |
| Events | create, patch |
| PersistentVolumeClaims | get, list, watch, create, update, patch, delete |
| ServiceAccounts | get, list, watch, create, update, patch, delete |
| Roles | get, list, watch, create, update, patch, delete |
| RoleBindings | get, list, watch, create, update, patch, delete |
MiniKube allows for simple local testing of Vespa on Kubernetes.
Initialize a Minikube cluster with 8 nodes, each with 4GiB of memory and 2 CPUs. Enable Minikube's image registry
add-on to allow the Minikube nodes to access the image. In this example, we use podman as the driver, though docker is also valid.
# Start Minikube using an insecure registry. This is not recommended for production. minikube start --nodes 8 --cpus 2 --memory 4GiB --driver=podman --insecure-registry="192.168.49.0/24" # Enable Image Registry add-on minikube addons enable registry # Verify MiniKube cluster was created minikube status
Cache the images provided by our support team locally.
# Authenticate to the registry echo $VESPAAI_REGISTRY_TOKEN | podman login images.ves.pa \ -u "$VESPAAI_REGISTRY_USER" \ --password-stdin # Cache the images locally podman pull images.ves.pa/kubernetes/vespa:$VESPA_VERSION podman pull images.ves.pa/kubernetes/operator:$VESPA_VERSION
Then, push the images to the MiniKube registry. The MiniKube registry is accessible from
$(minikube ip):5000 on a standard setup.
# Save the minikube registry endpoint $MINIKUBE_REGISTRY=$(minikube ip) # Push the kubernetes/vespa image to the registry podman tag kubernetes/vespa:$VESPA_VERSION $MINIKUBE_REGISTRY:5000/localhost/kubernetes/vespa:$VESPA_VERSION podman push --tls-verify=false $MINIKUBE_REGISTRY:5000/localhost/kubernetes/vespa:$VESPA_VERSION # Push the kubernetes/operator image to the registry podman tag kubernetes/operator:$VESPA_VERSION $MINIKUBE_REGISTRY:5000/localhost/kubernetes/operator:$VESPA_VERSION podman push --tls-verify=false $MINIKUBE_REGISTRY:5000/localhost/kubernetes/operator:$VESPA_VERSION
The images will now be available to the Minikube nodes from $MINIKUBE_REGISTRY:5000/localhost/kubernetes/operator:$VESPA_VERSION.
export OCI_IMAGE_REFERENCE=$MINIKUBE_REGISTRY:5000/localhost/kubernetes/vespa export OCI_IMAGE_REFERENCE_OPERATOR=$MINIKUBE_REGISTRY:5000/localhost/kubernetes/operator export OCI_IMAGE_TAG=$VESPA_VERSION
Install the Local Persistent Volume provisioner Helm Chart.
This will allow Persistent Volumes to be created in a MiniKube environment. The installation will automatically create a StorageClass called local-storage, which should be
used as the StorageClass for subsequent steps.
# Clone the local persistent volume static provisioner from the Kubernetes sigs $ git clone git@github.com:kubernetes-sigs/sig-storage-local-static-provisioner.git # Deploy onto the Kubernetes cluster $ helm install -f helm/examples/baremetal-default-storage.yaml local-volume-provisioner --namespace kube-system ./helm/provisioner
Create several usable volumes on each MiniKube Node. We recommend at least 4 per node for a smooth deployment.
# Create several volumes on each Minikube node.
$ for n in minikube minikube-m02 minikube-m03 minikube-m04 minikube-m05 minikube-m06 minikube-m07 minikube-m08; do
echo "==> $n"
minikube ssh -n "$n" -- '
set -e
for i in 1 2 3 4; do
sudo mkdir -p /mnt/disks/vol$i
if ! mountpoint -q /mnt/disks/vol$i; then
sudo mount --bind /mnt/disks/vol$i /mnt/disks/vol$i
fi
done
echo "Mounted:"
mount | grep -E "/mnt/disks/vol[1-4]" || true
'
done