StorageClasses that provision volumes before a pod is scheduled
What does ZopNight detect here?
ZopNight flags a StorageClass on EKS, GKE or AKS whose `volumeBindingMode` is `Immediate`. That mode, the default when the field is unset, provisions the volume the moment a PersistentVolumeClaim appears, before the scheduler has chosen a node, so a zonal disk can end up in a zone where the pod cannot run.
Signal and threshold
| Field | Value |
|---|---|
| Rule IDs | RC-1780 · RC-1880 · RC-1980 |
| Category | reliability |
| Severity | low |
| Metric | volumeBindingMode |
| Threshold | Immediate |
| Source | ZopNight |
| Permissions used | list storageclasses.storage.k8s.io |
Where it applies
Early provisioning ignores where the pod will run
The storage classes documentation
describes Immediate as binding and provisioning the volume as soon as the
PersistentVolumeClaim is created. For topology-constrained storage that is not reachable from
every node, the volume is created without knowing the pod’s scheduling requirements, which can
leave the pod unschedulable. On a multi-zone cluster backed by zonal storage, the disk can
appear in one zone while the only nodes that fit the pod are in another.
WaitForFirstConsumer avoids that. It delays binding and provisioning until a pod that uses the
claim is created, then provisions to match the pod’s constraints: resource requests, node
selectors, affinity and anti-affinity, and taints and tolerations. When the field is left out of
a StorageClass, the API server fills in Immediate.
Listing classes that bind immediately
kubectl get storageclasses -o json | jq -r ' .items[] | select((.volumeBindingMode // "Immediate") == "Immediate") | "\(.metadata.name) provisioner=\(.provisioner)"'kubectl get storageclass marks the cluster default with (default), which is the class most
new claims will use.
Reading one field on the class
ZopNight checks the binding mode recorded for each StorageClass and fires only when it reads
Immediate. A class set to WaitForFirstConsumer is left alone, as is a class where the mode was
not collected. Because the API server stores the default explicitly, classes that never set the
field still show Immediate and are flagged. The check does not look at the cluster’s zone layout.
Scope of the finding
The finding is about the class, not the claims already bound through it. Existing volumes stay where they were provisioned. Claims stuck waiting for a volume are reported by Unbound PVC, and classes that cannot grow their volumes by StorageClass disallows volume expansion.
Pods that cannot start, not a bill
There is no saving attached. The risk is StatefulSet or Deployment pods stuck Pending because
their freshly provisioned volume sits in a zone with no suitable node.
Switching to WaitForFirstConsumer
- Export the class:
kubectl get storageclass <name> -o yaml. volumeBindingModeis immutable, so create a new class, or delete and recreate this one, withvolumeBindingMode: WaitForFirstConsumerand the same provisioner and parameters.- If the old class was the default, move the
storageclass.kubernetes.io/is-default-class: "true"annotation to the new one and set the old one to"false". - Point new claims and StatefulSet volume claim templates at the new class.
- Avoid
nodeNamein pods that use it. WithWaitForFirstConsumer,nodeNamebypasses the scheduler and the claim stays pending; use akubernetes.io/hostnamenode selector instead.