New to KubeDB? Please start here.
Storage Autoscaling of an Etcd Cluster
This guide shows you how to use KubeDB to autoscale the storage of an etcd cluster.
Before You Begin
At first, you need a Kubernetes cluster, and the
kubectlcommand-line tool must be configured to communicate with your cluster.Install the
KubeDBProvisioner, Ops-manager and Autoscaler operators in your cluster following the steps here.etcd support is behind an alpha feature gate. Make sure the operators are installed with
Etcd=trueenabled (Helm valuefeatureGates.Etcd=true).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.You must have a
StorageClassthat supports volume expansion.You should be familiar with the following
KubeDBconcepts:
To keep everything isolated, we are going to use a separate namespace called demo throughout
this tutorial.
$ kubectl create ns demo
namespace/demo created
Note: YAML files used in this tutorial are stored in docs/examples/etcd folder in GitHub repository kubedb/docs.
Verify a volume-expandable StorageClass
First verify that your cluster has a storage class that supports volume expansion.
$ kubectl get storageclass
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
standard (default) rancher.io/local-path Delete WaitForFirstConsumer false 32m
topolvm-provisioner topolvm.cybozu.com Delete WaitForFirstConsumer true 31m
The topolvm-provisioner storage class has ALLOWVOLUMEEXPANSION set to true, so we can use
it. You can install topolvm from here.
Deploy Etcd
In this section we deploy a three member etcd cluster with version 3.6.4 on that storage class.
Below is the YAML of the Etcd CR that we are going to create,
apiVersion: kubedb.com/v1alpha2
kind: Etcd
metadata:
name: etcd-autoscale
namespace: demo
spec:
version: "3.6.4"
replicas: 3
storageType: Durable
storage:
storageClassName: "topolvm-provisioner"
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
deletionPolicy: WipeOut
Let’s create the Etcd CR we have shown above,
$ kubectl create -f https://github.com/kubedb/docs/raw/v2026.7.10/docs/examples/etcd/autoscaling/storage/etcd.yaml
etcd.kubedb.com/etcd-autoscale created
Now, wait until etcd-autoscale has the status Ready.
$ kubectl get etcd -n demo
NAME VERSION STATUS AGE
etcd-autoscale 3.6.4 Ready 3m
Let’s check the volume size from the PetSet and from the persistent volumes,
$ kubectl get petset -n demo etcd-autoscale -o json | jq '.spec.volumeClaimTemplates[].spec.resources.requests.storage'
"1Gi"
$ kubectl get pv -n demo
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS AGE
pvc-0a2f6b3a-6a3b-4a7e-8b58-1b0d6e0a1c11 1Gi RWO Delete Bound demo/data-etcd-autoscale-0 topolvm-provisioner 3m
pvc-2c7d1b90-2f11-4d0c-b7a1-9d1f2b3c4d55 1Gi RWO Delete Bound demo/data-etcd-autoscale-1 topolvm-provisioner 3m
pvc-5e91a4c2-77c9-41bb-9f3e-6a2c7d8e9f00 1Gi RWO Delete Bound demo/data-etcd-autoscale-2 topolvm-provisioner 3m
The PetSet requests 1Gi, and all three persistent volumes are 1Gi. We are now ready to apply the
EtcdAutoscaler CRO.
Storage Autoscaling
Create EtcdAutoscaler Object
To set up storage autoscaling for this etcd cluster, we create an EtcdAutoscaler CRO with the
desired configuration. Below is the YAML of the EtcdAutoscaler object that we are going to
create,
apiVersion: autoscaling.kubedb.com/v1alpha1
kind: EtcdAutoscaler
metadata:
name: etcd-storage-autoscaler
namespace: demo
spec:
databaseRef:
name: etcd-autoscale
storage:
etcd:
trigger: "On"
usageThreshold: 60
scalingThreshold: 50
expansionMode: "Online"
upperBound: 100Gi
Here,
spec.databaseRef.namespecifies that we are performing storage autoscaling on theetcd-autoscaledatabase.spec.storage.etcd.triggerspecifies that storage autoscaling is enabled for this database. The default isOff.spec.storage.etcd.usageThresholdspecifies the PVC usage threshold in percent. If no member’s volume is more than60%full, nothing happens.spec.storage.etcd.scalingThresholdspecifies by how much, in percent, the volume is grown once the usage threshold is crossed — here50%, so a 1Gi volume becomes roughly 1.5Gi.spec.storage.etcd.expansionModeselects the mode of the generated volume expansionEtcdOpsRequest:OnlineorOffline. UseOnlinewhen your CSI driver supports expanding a mounted volume, as topolvm does.spec.storage.etcd.upperBoundcaps the volume growth. If the newly computed size would exceed this, no ops request is created at all — so pick it deliberately.
Note: unlike some other KubeDB autoscalers,
EtcdAutoscalerdoes not currently ship a defaulting webhook, so the documented defaults for these fields are not filled in for you. Settrigger,usageThreshold,expansionModeand eitherscalingThresholdorscalingRulesexplicitly.
Using scalingRules instead of a flat percentage
A flat percentage is a poor fit once volumes get large — growing a 500Gi volume by 50% is a 250Gi
jump. scalingRules lets the growth step depend on how much is currently used. Rules are matched
in order of their appliesUpto size, and an entry with an empty appliesUpto acts as the
catch-all. A threshold ending in pc or % is relative; anything else is parsed as an absolute
quantity that is added to the current capacity.
apiVersion: autoscaling.kubedb.com/v1alpha1
kind: EtcdAutoscaler
metadata:
name: etcd-storage-autoscaler-rules
namespace: demo
spec:
databaseRef:
name: etcd-autoscale
storage:
etcd:
trigger: "On"
usageThreshold: 60
expansionMode: "Online"
upperBound: 500Gi
scalingRules:
- appliesUpto: "50Gi"
threshold: "50pc"
- appliesUpto: "200Gi"
threshold: "25pc"
- appliesUpto: ""
threshold: "50Gi"
That reads as: while less than 50Gi is used, grow by 50%; between 50Gi and 200Gi, grow by 25%;
beyond that, add a flat 50Gi at a time — never going past the 500Gi upperBound.
Let’s create the EtcdAutoscaler CR we showed first,
$ kubectl apply -f https://github.com/kubedb/docs/raw/v2026.7.10/docs/examples/etcd/autoscaling/storage/etcd-storage-autoscaler.yaml
etcdautoscaler.autoscaling.kubedb.com/etcd-storage-autoscaler created
Verify autoscaling is set up successfully
Let’s check that the etcdautoscaler resource was created successfully,
$ kubectl get etcdautoscaler -n demo
NAME AGE
etcd-storage-autoscaler 40s
$ kubectl describe etcdautoscaler etcd-storage-autoscaler -n demo
Name: etcd-storage-autoscaler
Namespace: demo
Labels: <none>
Annotations: <none>
API Version: autoscaling.kubedb.com/v1alpha1
Kind: EtcdAutoscaler
Metadata:
Creation Timestamp: 2026-02-11T10:02:11Z
Generation: 1
Resource Version: 52140
UID: 8a2b6c1e-4f7a-4d20-9b18-70c4f2d5a3b9
Spec:
Database Ref:
Name: etcd-autoscale
Storage:
Etcd:
Expansion Mode: Online
Scaling Threshold: 50
Trigger: On
Upper Bound: 100Gi
Usage Threshold: 60
Events: <none>
So the EtcdAutoscaler resource was created successfully.
Watch the generated EtcdOpsRequest
Now write enough data into the cluster that at least one member’s volume crosses the 60% usage
threshold. Once the Autoscaler operator observes that, it creates an EtcdOpsRequest of type
VolumeExpansion. Note the etcdops- name prefix, which is how the Autoscaler operator names
the requests it generates.
$ kubectl get etcdopsrequest -n demo
NAME TYPE STATUS AGE
etcdops-etcd-autoscale-w7q2rk VolumeExpansion Progressing 20s
Let’s wait for the ops request to become successful.
$ kubectl get etcdopsrequest -n demo
NAME TYPE STATUS AGE
etcdops-etcd-autoscale-w7q2rk VolumeExpansion Successful 2m
If we describe the EtcdOpsRequest, we get an overview of the steps that were followed to expand
the volumes. The owner reference back to the EtcdAutoscaler is what tells you this request was
generated rather than hand-written.
$ kubectl describe etcdopsrequest -n demo etcdops-etcd-autoscale-w7q2rk
Name: etcdops-etcd-autoscale-w7q2rk
Namespace: demo
Labels: app.kubernetes.io/component=database
app.kubernetes.io/instance=etcd-autoscale
app.kubernetes.io/managed-by=kubedb.com
app.kubernetes.io/name=etcds.kubedb.com
Annotations: <none>
API Version: ops.kubedb.com/v1alpha1
Kind: EtcdOpsRequest
Metadata:
Creation Timestamp: 2026-02-11T10:14:52Z
Generation: 1
Owner References:
API Version: autoscaling.kubedb.com/v1alpha1
Block Owner Deletion: true
Controller: true
Kind: EtcdAutoscaler
Name: etcd-storage-autoscaler
UID: 8a2b6c1e-4f7a-4d20-9b18-70c4f2d5a3b9
Resource Version: 53310
UID: b1e7d2a4-9c8f-4b12-a0d7-53f9c1e2b6aa
Spec:
Apply: IfReady
Database Ref:
Name: etcd-autoscale
Type: VolumeExpansion
Volume Expansion:
Etcd: 1610612736
Mode: Online
Status:
Conditions:
Last Transition Time: 2026-02-11T10:14:52Z
Message: Volume Expansion is in progress
Observed Generation: 1
Reason: Running
Status: True
Type: Running
Last Transition Time: 2026-02-11T10:14:55Z
Message: 3
Observed Generation: 1
Reason: PetSetReplicasBeforeExpansion
Status: True
Type: PetSetReplicasBeforeExpansion
Last Transition Time: 2026-02-11T10:16:38Z
Message: Successfully expanded the etcd data volumes
Observed Generation: 1
Reason: UpdateEtcdNodePVCs
Status: True
Type: UpdateEtcdNodePVCs
Last Transition Time: 2026-02-11T10:16:39Z
Message: Successfully updated the Etcd storage request
Observed Generation: 1
Reason: UpdateDatabase
Status: True
Type: UpdateDatabase
Last Transition Time: 2026-02-11T10:16:44Z
Message: PetSet has been recreated with the expanded volume claim template
Observed Generation: 1
Reason: ReadyPetSets
Status: True
Type: ReadyPetSets
Last Transition Time: 2026-02-11T10:16:45Z
Message: Successfully expanded the etcd data volumes
Observed Generation: 1
Reason: Successful
Status: True
Type: Successful
Observed Generation: 1
Phase: Successful
Events: <none>
The size in
spec.volumeExpansion.etcdis written as a plain byte count because the autoscaler computes it arithmetically from the observed capacity;1610612736is 1.5Gi.
Verify the new volume size
Now let’s verify from the PetSet and the PersistentVolumes that the volumes were expanded.
$ kubectl get petset -n demo etcd-autoscale -o json | jq '.spec.volumeClaimTemplates[].spec.resources.requests.storage'
"1610612736"
$ kubectl get pv -n demo
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS AGE
pvc-0a2f6b3a-6a3b-4a7e-8b58-1b0d6e0a1c11 2Gi RWO Delete Bound demo/data-etcd-autoscale-0 topolvm-provisioner 18m
pvc-2c7d1b90-2f11-4d0c-b7a1-9d1f2b3c4d55 2Gi RWO Delete Bound demo/data-etcd-autoscale-1 topolvm-provisioner 18m
pvc-5e91a4c2-77c9-41bb-9f3e-6a2c7d8e9f00 2Gi RWO Delete Bound demo/data-etcd-autoscale-2 topolvm-provisioner 18m
The output above confirms that the storage of the etcd cluster was autoscaled. (The reported PV capacity is rounded up to whatever unit the CSI driver allocates in, so it may be a little larger than the requested size.)
Cleaning Up
To clean up the Kubernetes resources created by this tutorial, run:
kubectl delete etcdautoscaler -n demo etcd-storage-autoscaler
kubectl delete etcd -n demo etcd-autoscale
kubectl delete ns demo
Next Steps
- Compute Resource Autoscaling of an Etcd cluster.
- Volume Expansion of an Etcd cluster — the same operation, applied by hand.
- Reclaim space inside the etcd backend with Compact and Defragment.
- Want to hack on KubeDB? Check our contribution guidelines.
































