Fix Kubernetes 'pod has unbound immediate PersistentVolumeClaims'

Quick answer
Your pod is stuck Pending with 'pod has unbound immediate PersistentVolumeClaims' because its PVC never bound to a PV. Here's how to find out why and fix each root cause.
- What this error means
- Step 1: See the actual error
- Cause 1: No default StorageClass and no dynamic provisioner
- Cause 2: storageClassName typo or a class that doesn't exist
- Cause 3: WaitForFirstConsumer — Pending is normal (for now)
9 min read · Kubernetes
Fix Kubernetes 'pod has unbound immediate PersistentVolumeClaims'
You deploy a workload and the pod never schedules. kubectl describe pod shows this event:
Warning FailedScheduling pod has unbound immediate PersistentVolumeClaims
The pod is waiting on a PersistentVolumeClaim that hasn't been bound to a PersistentVolume yet. Until that PVC binds, the scheduler refuses to place the pod. This guide walks through why a PVC stays unbound and how to fix each cause.
What this error means
A pod that mounts a PVC can only be scheduled once that PVC is Bound to an actual PersistentVolume. "Immediate" refers to the PVC's binding mode: with volumeBindingMode: Immediate, Kubernetes tries to bind (or dynamically provision) the volume the moment the PVC is created — independent of any pod.
If that binding never happens, the PVC sits in Pending, the pod sits in Pending, and the scheduler emits "pod has unbound immediate PersistentVolumeClaims". The problem is almost always with the PVC, its StorageClass, or the available PVs — not the pod itself.
Step 1: See the actual error
Start at the PVC, not the pod. Check its status first:
kubectl get pvcNAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
data-web Pending standard 3m
A Pending PVC is your smoking gun. Now read its events — this is where Kubernetes tells you exactly what went wrong:
kubectl describe pvc data-webLook at the Events and the StorageClass field. Then list your StorageClasses so you know what provisioners exist:
kubectl get storageclassKeep these three commands handy — nearly every cause below is diagnosed from their output.
Cause 1: No default StorageClass and no dynamic provisioner
The most common cause. Your PVC omits storageClassName, so Kubernetes looks for the default StorageClass to provision a volume dynamically — and there isn't one.
What you'll see: the PVC just sits Pending with no provisioning event at all — because with no default StorageClass and no storageClassName, the controller never picks a provisioner to try. The tell is kubectl get storageclass showing no class marked (default):
kubectl get storageclass
# NAME PROVISIONER ...
# (no row marked "(default)") -> nothing will dynamically provision(If you instead see a ProvisioningFailed: no volume plugin matched event, a StorageClass did match but its provisioner isn't installed in the cluster — see the CSI-driver note at the end of this section.)
Fix: Mark an existing StorageClass as default:
kubectl patch storageclass standard \
-p '{"metadata":{"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'If you have no StorageClass at all (bare-metal or a stripped cluster), you need a CSI driver / provisioner installed first — for example the AWS EBS CSI driver, or a software-defined backend like Rook-Ceph, Longhorn, or OpenEBS. Once installed, they register a StorageClass you can set as default.
Cause 2: storageClassName typo or a class that doesn't exist
The PVC names a StorageClass that isn't in the cluster. Kubernetes has nothing to provision from, so the PVC hangs forever.
What you'll see: the PVC's STORAGECLASS column shows a name that doesn't appear in kubectl get storageclass.
Fix: Correct the name to match a real class:
1apiVersion: v1
2kind: PersistentVolumeClaim
3metadata:
4 name: data-web
5spec:
6 storageClassName: standard # must match `kubectl get storageclass`
7 accessModes: ["ReadWriteOnce"]
8 resources:
9 requests:
10 storage: 10GiWatch the semantics of empty vs unset:
storageClassNameunset (field absent): use the default StorageClass.storageClassName: ""(empty string): disable dynamic provisioning entirely — bind only to a pre-existing PV with no class. If you set""by accident and have no matching static PV, the PVC stays Pending.
Cause 3: WaitForFirstConsumer — Pending is normal (for now)
If your StorageClass uses volumeBindingMode: WaitForFirstConsumer, the PVC is supposed to stay Pending until a pod that consumes it is scheduled. Binding and provisioning are deferred so the volume lands in the same zone/node as the pod.
Check the mode:
kubectl get storageclass standard -o jsonpath='{.volumeBindingMode}'WaitForFirstConsumer: a lone PVC with no pod will readPending— that's expected. It binds once a pod references it and the scheduler picks a node. Note: with this mode you won't get the "unbound immediate" wording; the pod event will instead saywaiting for first consumer to be created before binding. If your PVC is stuck here, the real blocker is usually why the pod can't schedule (node affinity, taints, resources).Immediate: the PVC should bind right away. If it doesn't, it's one of the other causes here.
Fix: For WaitForFirstConsumer, deploy the pod that uses the PVC and make sure that pod is actually schedulable. Don't "fix" a Pending PVC that's simply waiting for its consumer.
Stuck on this in production?
We debug exactly this kind of issue for platform teams — usually in a single working session.
Cause 4: Static provisioning — no PV matches the claim
If you provision PVs by hand (no dynamic provisioner), a PVC only binds to a PV that satisfies all of its requirements: capacity, access modes, StorageClass, and any selector labels. One mismatch and it stays unbound.
Common mismatches:
- PVC requests
20Gibut every free PV is10Gi(a PV must be at least the requested size). - PVC wants
ReadWriteManybut the PV only offersReadWriteOnce. - PVC has a
selectorwhose labels no PV carries. - The PV is already
BoundorReleased, notAvailable.
kubectl get pv # check STATUS, CAPACITY, ACCESS MODES, STORAGECLASSFix: Create a PV that matches the claim exactly:
1apiVersion: v1
2kind: PersistentVolume
3metadata:
4 name: data-web-pv
5spec:
6 capacity:
7 storage: 10Gi
8 accessModes: ["ReadWriteOnce"] # must cover what the PVC asks
9 storageClassName: standard # must match the PVC
10 hostPath:
11 path: /mnt/data/web # or nfs, csi, etc.Cause 5: CSI driver not running or a topology/zone mismatch
The StorageClass exists, but the volume can't actually be provisioned or attached.
-
CSI driver pods are down: the provisioner isn't there to answer.
bashkubectl get pods -n kube-system | grep -i csi kubectl get csidrivers -
Zone/topology mismatch: a dynamically provisioned volume lives in one availability zone, but no schedulable node is in that zone (common on cloud with
Immediatebinding across multi-AZ clusters). The PV provisions but nothing can mount it. Switching that StorageClass toWaitForFirstConsumerfixes this by provisioning the volume in the pod's zone.
Fix: Restart / repair the CSI driver, and prefer WaitForFirstConsumer for zonal block storage so the volume follows the pod.
Cause 6: A ResourceQuota is blocking the claim
If the namespace has a ResourceQuota limiting persistentvolumeclaims count or requests.storage, a new claim over the limit is rejected and never provisions.
What you'll see:
Error: exceeded quota: storage-quota, requested: requests.storage=10Gi,
used: requests.storage=50Gi, limited: requests.storage=50Gi
Fix: Inspect and raise the quota (or clean up unused PVCs):
kubectl get resourcequota -n <namespace>
kubectl describe resourcequota storage-quota -n <namespace>Quick reference
# The three commands that diagnose almost every unbound PVC
kubectl get pvc # Is it Pending?
kubectl describe pvc <name> # Events explain why
kubectl get storageclass # Is there a default? What binding mode?| Symptom | Likely cause |
|---|---|
No (default) StorageClass | Cause 1 — set a default or install a provisioner |
PVC's class not in get storageclass | Cause 2 — typo / missing class |
WaitForFirstConsumer mode | Cause 3 — normal; schedule the pod |
No matching Available PV | Cause 4 — create a matching static PV |
| CSI pods down / wrong zone | Cause 5 — repair driver, use WaitForFirstConsumer |
exceeded quota in events | Cause 6 — raise the ResourceQuota |
Frequently Asked Questions
Why is my PVC stuck in Pending?
Because it hasn't found a PersistentVolume to bind to. Either no default StorageClass exists to provision one dynamically, the named class is wrong, no static PV matches the request, or the class uses WaitForFirstConsumer and is legitimately waiting for a pod. kubectl describe pvc <name> shows the exact reason in its Events.
What is the difference between Immediate and WaitForFirstConsumer binding?
Immediate binds (and provisions) the volume as soon as the PVC is created, regardless of any pod. WaitForFirstConsumer delays binding until a pod that uses the PVC is scheduled, so the volume is placed in the same node or zone as the pod. Use WaitForFirstConsumer for zonal block storage to avoid topology mismatches.
How do I set a default StorageClass?
Patch the class with the default annotation: kubectl patch storageclass <name> -p '{"metadata":{"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'. Make sure only one class is marked default — if two are, Kubernetes ignores the annotation and treats the PVC as if no default exists.
Does an empty storageClassName behave the same as leaving it unset?
No. Leaving storageClassName unset makes the PVC use the default StorageClass. Setting it to "" (empty string) disables dynamic provisioning and forces the PVC to bind only to a pre-existing PV that also has no class. Confusing the two is a common reason a PVC never binds.
Why does my PVC stay Pending even though a PersistentVolume exists?
The PV probably doesn't match the claim. A PV must satisfy every requirement: capacity at or above the request, matching access modes, the same StorageClass, and any label selector on the PVC. Also confirm the PV's status is Available rather than Bound or Released. Compare kubectl get pv against kubectl describe pvc <name>.
See also
- PersistentVolume vs PersistentVolumeClaim: The Difference, Explained Properly
- Kubernetes Storage: PV, PVC and StorageClass explained — how the three objects fit together
- Persistent Volumes in Production — access modes, reclaim policies, and binding modes done right
- Rook-Ceph vs Longhorn vs OpenEBS — picking a storage backend that ships a working default StorageClass
Still fighting a PVC that won't bind? Talk to us at Coding Protocols — we design Kubernetes storage that provisions cleanly on the first deploy.
Official References
- Persistent Volumes — access modes, reclaim policies and binding
- Storage Classes — provisioner parameters and volume binding modes
- Debug Pods — reading pod status, events and container states
- kubectl reference — command syntax, output formats and selectors
Was this article helpful?
Be the first to rate this article
Related Topics
Found this useful? Share it.


