Skip to content

PersistentVolumes & StorageClasses

Understand the Kubernetes storage layer: PVs, PVCs, and dynamic provisioning.

Time: ~10 minutes Difficulty: Intermediate

  • PersistentVolume (PV): a piece of storage in the cluster
  • PersistentVolumeClaim (PVC): a request for storage by a pod
  • StorageClass: dynamic provisioning without pre-creating PVs
  • Access modes: ReadWriteOnce, ReadOnlyMany, ReadWriteMany
  • Reclaim policies: Retain vs Delete
  • Data persistence across pod restarts
Terminal window
kubectl get storageclasses

Minikube provides a standard StorageClass backed by hostPath.

Navigate to the demo directory:

Terminal window
cd demos/persistent-volumes
Terminal window
kubectl apply -f manifests/namespace.yaml
kubectl apply -f manifests/static-pv.yaml

Check the PV and PVC:

Terminal window
kubectl get pv
kubectl get pvc -n storage-demo

The PVC should be Bound to the PV.

Terminal window
kubectl apply -f manifests/dynamic-pvc.yaml
Terminal window
kubectl get pvc -n storage-demo
kubectl get pv

A new PV is automatically created by the standard StorageClass and bound to the PVC.

Terminal window
kubectl apply -f manifests/writer-pod.yaml
kubectl logs writer -n storage-demo -f

The writer appends a log entry every 5 seconds to /data/log.txt on the PVC.

Delete the writer and verify data persists

Section titled “Delete the writer and verify data persists”
Terminal window
kubectl delete pod writer -n storage-demo
# Data is still on the PVC
kubectl run verify --rm -it --image=busybox:1.36 -n storage-demo \
--overrides='{"spec":{"volumes":[{"name":"d","persistentVolumeClaim":{"claimName":"dynamic-pvc"}}],"containers":[{"name":"v","image":"busybox:1.36","command":["cat","/data/log.txt"],"volumeMounts":[{"name":"d","mountPath":"/data"}]}]}}'

The data survives pod deletion because it lives on the PVC, not in the container.

Terminal window
kubectl apply -f manifests/reader-pod.yaml
kubectl logs reader -n storage-demo
manifests/
namespace.yaml # storage-demo namespace
static-pv.yaml # Manual PV (hostPath) + PVC with label selector
dynamic-pvc.yaml # PVC using standard StorageClass (PV auto-created)
writer-pod.yaml # Writes to dynamic-pvc
reader-pod.yaml # Reads from manual-pvc

Storage hierarchy:

StorageClass (how to provision)
|
v
PersistentVolume (a piece of storage)
|
v (bound)
PersistentVolumeClaim (a request for storage)
|
v (mounted)
Pod

Access modes:

ModeShortDescription
ReadWriteOnceRWOOne node can mount read-write
ReadOnlyManyROXMany nodes can mount read-only
ReadWriteManyRWXMany nodes can mount read-write

Reclaim policies:

PolicyWhat Happens When PVC Is Deleted
RetainPV keeps data, admin must clean up manually
DeletePV and underlying storage are deleted
RecycleDeprecated. Was rm -rf /volume/*
  1. Check what backing storage the dynamic PV uses:

    Terminal window
    kubectl get pv -o wide
    kubectl describe pv <pv-name>
  2. Delete the PVC and check the PV reclaim behavior:

    Terminal window
    kubectl delete pvc dynamic-pvc -n storage-demo
    kubectl get pv

    Dynamic PVs with Delete policy are removed. Static PVs with Retain stay.

  3. List StorageClass details:

    Terminal window
    kubectl describe storageclass standard
Terminal window
kubectl delete namespace storage-demo
kubectl delete pv manual-pv --ignore-not-found

See docs/deep-dive.md for a detailed explanation of CSI drivers, volume snapshots, volume expansion, topology-aware provisioning, and production storage patterns.

Move on to Vault to learn production secret management with HashiCorp Vault.