Kubernetes - PersistentVolume Static Binding Patterns
In static provisioning an administrator creates PVs manually, before any PVC requests one. When a PVC is created, Kubernetes looks for an existing PV with enough capacity, a compatible access mode, and a matching storageClassName, and binds the two together. No provisioner runs and no PV gets created automatically.
This challenge covers three patterns that control how that match happens, plus the WaitForFirstConsumer binding mode for node-local storage.
Task 1 - Generic Match
Setting storageClassName: "" on both the PV and PVC bypasses the DefaultStorageClass admission controller and dynamic provisioning. Kubernetes binds the PVC to the first available PV that has sufficient capacity and a matching access mode.
Omitting storageClassName is not the same as setting it to "". A PVC without the key gets the default injected at admission time, and the control plane keeps it in sync if the default StorageClass changes later. Setting storageClassName: "" opts out of that behavior. The PVC keeps its empty class no matter what the cluster default is.
Steps:
- Create a PV named
pv-genericwithstorageClassName: "", typehostPath, path/mnt/static/generic, capacity500Mi, access modeReadWriteOnce - Create a PVC named
pvc-genericwithstorageClassName: "", capacity500Mi, access modeReadWriteOnce - Observe
pvc-genericbind topv-generic
Task 2 - Label Selector Match
A PVC can narrow which PV it binds to using spec.selector.matchLabels. Kubernetes only considers PVs that carry all the listed labels. This lets you target a specific tier or quality of pre-provisioned storage without using separate StorageClass names.
Steps:
- Create a PV named
pv-selectorwith labeldisk-type: fast,storageClassName: "", typehostPath, path/mnt/static/selector, capacity500Mi, access modeReadWriteOnce - Create a PVC named
pvc-selectorwithstorageClassName: "", a label selector that matches PVs labeleddisk-type: fast, capacity500Mi, access modeReadWriteOnce - Observe
pvc-selectorbind topv-selector
Task 3 - Bidirectional Binding
When Kubernetes binds a PVC to a PV it writes claimRef on the PV to mark it as taken. You can set claimRef manually before the PVC exists to pre-reserve the PV - no other claim can take it while it waits. The other side of the lock is spec.volumeName on the PVC, which ensures the PVC only binds to that specific PV and not to any other eligible one.
Steps:
- Create a PV named
pv-bidirwith aclaimRefthat points to a PVC namedpvc-bidirin thedefaultnamespace (bothnameandnamespaceare required onclaimRef, otherwise the PV cannot match the PVC),storageClassName: "", typehostPath, path/mnt/static/bidir, capacity500Mi, access modeReadWriteOnce - Create a PVC named
pvc-bidirwithvolumeNameset topv-bidir,storageClassName: "", capacity500Mi, access modeReadWriteOnce - Observe both sides bound to each other
Task 4 - Local PV with WaitForFirstConsumer
hostPath PVs have no node affinity. The scheduler does not know which node the data is on and can place a pod anywhere, causing a mount failure if the pod lands on the wrong node.
The local volume type requires spec.nodeAffinity, which tells the scheduler exactly where the data lives. Pairing it with a StorageClass that uses volumeBindingMode: WaitForFirstConsumer delays PVC binding until a pod requests it. The scheduler picks a node for the pod first, checks that a PV exists there, then binds the PVC to that PV.
Steps:
- Create a StorageClass
sc-local-wfcwithprovisioner: kubernetes.io/no-provisionerandvolumeBindingMode: WaitForFirstConsumer - Create a PV
pv-localof typelocal, path/mnt/disks/local,storageClassName: sc-local-wfc,nodeAffinitypinned tocplane-01, capacity1Gi, access modeReadWriteOnce - Create a PVC
pvc-localwithstorageClassName: sc-local-wfc, capacity1Gi, access modeReadWriteOnce - Confirm
pvc-localstays Pending despite the matching PV existing - Create a pod
local-pod(imagebusybox, commandsleep 3600) consumingpvc-localat/data, nonodeSelectorornodeName - Confirm the pod lands on
cplane-01andpvc-localbinds