Kubernetes
9 min readJuly 2, 2026Updated August 19, 2026

Fix Kubernetes 'pod has unbound immediate PersistentVolumeClaims'

AJ
Ajeet Yadav
Platform & Cloud Engineer
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.

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:

bash
kubectl get pvc
NAME       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:

bash
kubectl describe pvc data-web

Look at the Events and the StorageClass field. Then list your StorageClasses so you know what provisioners exist:

bash
kubectl get storageclass

Keep 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):

bash
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:

bash
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:

yaml
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: 10Gi

Watch the semantics of empty vs unset:

  • storageClassName unset (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:

bash
kubectl get storageclass standard -o jsonpath='{.volumeBindingMode}'
  • WaitForFirstConsumer: a lone PVC with no pod will read Pending — 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 say waiting 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.

Talk to us

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 20Gi but every free PV is 10Gi (a PV must be at least the requested size).
  • PVC wants ReadWriteMany but the PV only offers ReadWriteOnce.
  • PVC has a selector whose labels no PV carries.
  • The PV is already Bound or Released, not Available.
bash
kubectl get pv     # check STATUS, CAPACITY, ACCESS MODES, STORAGECLASS

Fix: Create a PV that matches the claim exactly:

yaml
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.

    bash
    kubectl 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 Immediate binding across multi-AZ clusters). The PV provisions but nothing can mount it. Switching that StorageClass to WaitForFirstConsumer fixes 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):

bash
kubectl get resourcequota -n <namespace>
kubectl describe resourcequota storage-quota -n <namespace>

Quick reference

bash
# 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?
SymptomLikely cause
No (default) StorageClassCause 1 — set a default or install a provisioner
PVC's class not in get storageclassCause 2 — typo / missing class
WaitForFirstConsumer modeCause 3 — normal; schedule the pod
No matching Available PVCause 4 — create a matching static PV
CSI pods down / wrong zoneCause 5 — repair driver, use WaitForFirstConsumer
exceeded quota in eventsCause 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

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

Was this article helpful?

Be the first to rate this article

Related Topics

Kubernetes
Troubleshooting
Storage
PersistentVolume
PVC

Found this useful? Share it.

Practice this

Related tools

Read Next