New to KubeDB? Please start here.
Milvus Storage Autoscaling
This guide will show you how to use the KubeDB Autoscaler operator to autoscale the persistent storage of a Milvus database.
Before You Begin
You should be familiar with the following
KubeDBconcepts:Install the KubeDB Autoscaler operator.
During KubeDB installation, enable the KubeDB storage metrics server by passing the following Helm flag:
--set kubedb-autoscaler.storage-metrics-server.enabled=trueIt provides the custom metrics API (
custom.metrics.k8s.io) backed by the KubeDB storage-metrics apiserver. The storage autoscaler reads PVC usage from this API. It is disabled by default. To enable it on an existing installation:$ helm upgrade kubedb oci://ghcr.io/appscode-charts/kubedb \ --namespace kubedb --reuse-values \ --set kubedb-autoscaler.storage-metrics-server.enabled=trueCheck that the API is served. The autoscaler queries
custom.metrics.k8s.io/v1beta2; aNotFounderror here means the storage metrics server is not running:$ kubectl get --raw /apis/custom.metrics.k8s.io/v1beta2Without this API the storage autoscaler never triggers. The autoscaler’s status and events stay empty; only the KubeDB autoscaler operator’s log shows
custom metrics API returned 404 Not Found.The PVC’s
StorageClassmust support volume expansion (allowVolumeExpansion: true) — e.g.longhorn-custom.Complete the dependency setup from Prepare Dependencies. It installs MinIO and creates the object-storage secrets (
milvus-storage-configformilvus-standalone,milvus-cluster-storage-configformilvus-cluster).
Note: The yaml files used in this tutorial are stored in docs/guides/milvus/autoscaler/storage/yamls folder in GitHub repository kubedb/docs.
Storage Autoscaling — Standalone Milvus
Deploy a standalone Milvus on an expansion-capable StorageClass. The volume expansion guide’s milvus-standalone.yaml is the standalone quickstart manifest with storageClassName: longhorn-custom and a 1Gi volume:
If you deleted a Milvus of the same name in an earlier guide, delete its object-storage prefix first, as shown in Prepare Dependencies. Otherwise the new database can hang in
Provisioning.
$ kubectl apply -f https://github.com/kubedb/docs/raw/v2026.10.12/docs/guides/milvus/volume-expansion/yamls/milvus-standalone.yaml
milvus.kubedb.com/milvus-standalone created
$ kubectl get milvuses.kubedb.com -n demo milvus-standalone -w
Then create a MilvusAutoscaler targeting the standalone node:
storage-standalone.yaml
apiVersion: autoscaling.kubedb.com/v1alpha1
kind: MilvusAutoscaler
metadata:
name: milvus-storage-autoscaler
namespace: demo
spec:
databaseRef:
name: milvus-standalone
storage:
node:
trigger: "On"
usageThreshold: 30
expansionMode: "Offline"
scalingRules:
- appliesUpto: "100Ti"
threshold: "50%"
opsRequestOptions:
apply: IfReady
timeout: 10m
$ kubectl apply -f https://github.com/kubedb/docs/raw/v2026.10.12/docs/guides/milvus/autoscaler/storage/yamls/storage-standalone.yaml
milvusautoscaler.autoscaling.kubedb.com/milvus-storage-autoscaler created
When the volume usage crosses usageThreshold (30%), the autoscaler creates a VolumeExpansion MilvusOpsRequest sized per scalingRules.
$ kubectl get milvusautoscaler -n demo
NAME AGE
milvus-storage-autoscaler 93s
You can query the same metric the autoscaler uses for the PVC:
$ kubectl get --raw /apis/custom.metrics.k8s.io/v1beta2/namespaces/demo/persistentvolumeclaims/data-milvus-standalone-0/volume_used_percentage
The storage autoscaler watches the streamingnode/node PVC usage through the custom.metrics.k8s.io API backed by the KubeDB storage-metrics apiserver. When usage crosses usageThreshold, it creates a VolumeExpansion MilvusOpsRequest. In this walkthrough the volume stayed well below the threshold (a freshly-created, near-empty 1Gi volume), so no expansion was triggered:
$ kubectl get pvc -n demo -l app.kubernetes.io/instance=milvus-standalone -o custom-columns=NAME:.metadata.name,SIZE:.status.capacity.storage
NAME SIZE
data-milvus-standalone-0 1Gi
To see the expansion fire, write enough data to push PVC usage past
usageThreshold(30% here). For a quick test, write a 400MiB file into the data volume (about 39% of1Gi), and remove it afterwards:$ kubectl exec -n demo milvus-standalone-0 -c milvus -- fallocate -l 400M /var/lib/milvus/fill.binWhen it does, the autoscaler creates a
VolumeExpansionMilvusOpsRequest(withexpansionModeas configured), which the Ops-manager applies — see the volume expansion guide for the resulting flow and output.
Storage Autoscaling — Distributed Milvus
For a distributed Milvus, storage autoscaling targets streamingnode — the only distributed role with a persistent volume.
Deploy a distributed Milvus on longhorn-custom, for example with the volume expansion guide’s milvus-distributed.yaml (two streamingnode replicas with 1Gi each). The distributed autoscaler reuses the name milvus-storage-autoscaler, so delete the standalone one first:
$ kubectl apply -f https://github.com/kubedb/docs/raw/v2026.10.12/docs/guides/milvus/volume-expansion/yamls/milvus-distributed.yaml
milvus.kubedb.com/milvus-cluster created
$ kubectl get milvuses.kubedb.com -n demo milvus-cluster -w
$ kubectl delete milvusautoscaler -n demo milvus-storage-autoscaler
storage-distributed.yaml
apiVersion: autoscaling.kubedb.com/v1alpha1
kind: MilvusAutoscaler
metadata:
name: milvus-storage-autoscaler
namespace: demo
spec:
databaseRef:
name: milvus-cluster
storage:
streamingnode:
trigger: "On"
usageThreshold: 34
expansionMode: "Online"
scalingRules:
- appliesUpto: "100Ti"
threshold: "50%"
opsRequestOptions:
apply: IfReady
timeout: 10m
$ kubectl apply -f https://github.com/kubedb/docs/raw/v2026.10.12/docs/guides/milvus/autoscaler/storage/yamls/storage-distributed.yaml
milvusautoscaler.autoscaling.kubedb.com/milvus-storage-autoscaler created
Because only streamingnode carries a persistent volume among the distributed roles, storage autoscaling targets streamingnode exclusively. When the streamingnode PVC usage crosses usageThreshold (34% here), the autoscaler creates a VolumeExpansion MilvusOpsRequest against streamingnode (expansionMode: Online), which the Ops-manager applies as shown in the volume expansion guide.
Cleaning up
$ kubectl get milvusopsrequest -n demo # wait until none is Progressing
$ kubectl delete milvusautoscaler -n demo --all
$ kubectl patch -n demo milvus.kubedb.com milvus-standalone -p '{"spec":{"deletionPolicy":"WipeOut"}}' --type="merge"
$ kubectl patch -n demo milvus.kubedb.com milvus-cluster -p '{"spec":{"deletionPolicy":"WipeOut"}}' --type="merge"
$ kubectl delete milvus.kubedb.com -n demo milvus-standalone milvus-cluster
$ kubectl delete ns demo
Next Steps
- Learn about compute autoscaling.
- Want to hack on KubeDB? Check our contribution guidelines.
































