Kubernetes local volumes: When and how to use them
Learn how to create Kubernetes local volumes using StorageClass, PersistentVolume and PersistentVolumeClaim. Covers static provisioning, WaitForFirstConsumer binding, and when local volumes make sense.
5 lessons · 16 min · Intermediate
Written by
Marketing Team at Civo
Written by
Marketing Team at Civo
Local volumes give pods direct access to node-local storage while remaining fully managed by Kubernetes. Unlike hostPath volumes, local volumes participate in the scheduler's topology awareness — Kubernetes knows which nodes have local volumes available and schedules pods accordingly.
When to use local volumes
Use a local volume when your workload needs the performance of directly attached storage and cannot tolerate the latency of network-backed storage. The most common use case is databases with high IOPS requirements such as etcd, Cassandra, or PostgreSQL, where network storage introduces unacceptable latency.
If your workload does not need node-local performance, use dynamic provisioning with a PVC and StorageClass instead. It is portable, scales automatically, and is easier to operate.
How this demo works
This demo uses hostPath as the underlying storage path for simplicity. In production, local volumes should be backed by dedicated block devices or separate disk partitions, not the host's general filesystem. Using the root filesystem as local volume backing in production can exhaust disk space and destabilise the node.
The demo creates: a StorageClass with no-provisioner, a manually created PV pointing to /opt/data on the node, a PVC that requests storage from that StorageClass, and a pod that mounts the PVC.
Step 1: Create the StorageClass
Local volumes do not support dynamic provisioning. The StorageClass is created with provisioner: kubernetes.io/no-provisioner and volumeBindingMode: WaitForFirstConsumer. The WaitForFirstConsumer mode delays PVC binding until a pod that uses it is scheduled, which allows the scheduler to factor in node constraints before committing the PVC to a specific PV.
apiVersion: storage.k8s.io/v1kind: StorageClassmetadata:name: local-storageprovisioner: kubernetes.io/no-provisionervolumeBindingMode: WaitForFirstConsumer
kubectl create -f sc.yaml
Verify:
kubectl get sc
Expected output:
NAME PROVISIONER VOLUMEBINDINGMODElocal-storage kubernetes.io/no-provisioner WaitForFirstConsumer
Step 2: Create the PersistentVolume
apiVersion: v1kind: PersistentVolumemetadata:name: demo-pvspec:storageClassName: local-storagecapacity:storage: 1GiaccessModes:- ReadWriteOncehostPath:path: /opt/datapersistentVolumeReclaimPolicy: RetainnodeAffinity:required:nodeSelectorTerms:- matchExpressions:- key: kubernetes.io/hostnameoperator: Invalues:- <your-node-name>
Replace <your-node-name> with the name of the node where /opt/data exists. Run kubectl get nodes to find your node name.
kubectl create -f pv.yaml
Step 3: Create the PersistentVolumeClaim
apiVersion: v1kind: PersistentVolumeClaimmetadata:name: demo-pvclaimspec:storageClassName: local-storageaccessModes:- ReadWriteOnceresources:requests:storage: 512Mi
kubectl create -f pvc.yaml
Check the PV and PVC status immediately after creation:
kubectl get pv,pvc
Expected output at this point:
NAME CAPACITY ACCESS MODES STATUS STORAGECLASSpersistentvolume/demo-pv 1Gi RWO Available local-storageNAME STATUS STORAGECLASSpersistentvolumeclaim/demo-pvclaim Pending local-storage
The PVC stays in Pending status because WaitForFirstConsumer delays binding until a pod is scheduled. This is expected and correct behaviour.
Step 4: Create a pod that uses the PVC
apiVersion: v1kind: Podmetadata:name: task-pv-podspec:volumes:- name: pv-volumepersistentVolumeClaim:claimName: demo-pvclaimcontainers:- name: nginximage: nginxvolumeMounts:- mountPath: /usr/share/nginx/htmlname: pv-volume
kubectl create -f pod.yaml
Now check the PV and PVC status again:
kubectl get pv,pvc
Expected output after the pod is scheduled:
NAME CAPACITY ACCESS MODES STATUS STORAGECLASSpersistentvolume/demo-pv 1Gi RWO Bound local-storageNAME STATUS VOLUME CAPACITY STORAGECLASSpersistentvolumeclaim/demo-pvclaim Bound demo-pv 1Gi local-storage
Both are now Bound. The binding happened when the scheduler assigned the pod to a node.
Check which node the pod is running on:
kubectl get pods -o wide
Step 5: Write data and verify it on the node
Exec into the pod:
kubectl exec -it task-pv-pod -- sh
Create a file:
cd /usr/share/nginx/htmlecho "hello" >> index.htmlcurl localhost
Expected output:
hello
Exit the pod. On the node where the pod is running, the file is now visible at /opt/data/index.html.
Step 6: Verify data persists after pod deletion
Delete the pod:
kubectl delete pod task-pv-pod --force --grace-period=0
Check the PV and PVC:
kubectl get pv,pvc
Expected output:
NAME STATUS STORAGECLASSpersistentvolume/demo-pv Bound local-storageNAME STATUS STORAGECLASSpersistentvolumeclaim/demo-pvclaim Bound local-storage
Both remain. The data on disk is still at /opt/data/index.html on the node. The pod is gone but the storage objects and their data are independent of it.
Delete the PVC:
kubectl delete persistentvolumeclaim/demo-pvclaim
Check the PV:
kubectl get pv
Expected output:
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUSdemo-pv 1Gi RWO Retain Released
Because the reclaim policy is Retain, the PV moves to Released status rather than being deleted. The data on disk is preserved. To reuse the PV it must be manually reclaimed.

Marketing Team at Civo
Civo is the Sovereign Cloud and AI platform designed to help developers and enterprises build without limits. We bridge the gap between the openness of the public cloud and the rigorous security of private environments, delivering full cloud parity across every deployment. As a team, we are dedicated to providing scalable compute, lightning-fast Kubernetes, and managed services that are ready in minutes. Through CivoStack Enterprise and our FlexCore appliance, we empower organizations to maintain total data sovereignty on their own hardware.
Our mission is to make the cloud faster, simpler, and fairer. By providing enterprise-grade NVIDIA GPUs and streamlined model management, we ensure that high-performance AI and machine learning are accessible to everyone. Built for transparency and performance, the Civo Team is here to give you total control over your infrastructure, your data, and your spend.
Share this lesson