Skip to content
DevOps AI ToolKit
Newsletter
All guides
AI for Kubernetes & Helm By James Joyner IV · · 9 min read

Kubernetes Error Guide: 'pod has unbound immediate PersistentVolumeClaims' PVC Pending

Fix unbound PersistentVolumeClaims in Kubernetes: resolve missing StorageClass, WaitForFirstConsumer binding, storageClassName mismatch, and CSI provisioner failures.

  • #kubernetes
  • #troubleshooting
  • #errors
  • #storage

Exact Error Message

A pod that mounts a PersistentVolumeClaim stays Pending, and the scheduler reports the claim is unbound:

Events:
  Type     Reason            Age   From               Message
  ----     ------            ----  ----               -------
  Warning  FailedScheduling  12s   default-scheduler  0/3 nodes are available: pod has unbound immediate PersistentVolumeClaims. preemption: 0/3 nodes are available.

The PVC itself stays Pending, and kubectl describe pvc shows one of these messages depending on the cause:

# No provisioner / no default StorageClass:
Warning  ProvisioningFailed  no persistent volumes available for this claim and no storage class is set

# Expected behaviour with WaitForFirstConsumer:
Normal   WaitForFirstConsumer  waiting for first consumer to be created before binding

What the Error Means

A PersistentVolumeClaim is a request for storage. It must bind to a PersistentVolume before any pod can use it. Until binding succeeds the PVC stays Pending, and any pod mounting it cannot be scheduled — that surfaces as pod has unbound immediate PersistentVolumeClaims on the pod.

There are two binding modes, and they behave very differently:

  • Immediate — the PVC binds (or dynamically provisions a PV) as soon as it is created. pod has unbound immediate PersistentVolumeClaims means an Immediate-mode PVC failed to bind, so something is genuinely wrong.
  • WaitForFirstConsumer (WFFC) — binding is deliberately delayed until a pod that uses the PVC is scheduled, so the volume can be provisioned in the right zone. waiting for first consumer to be created before binding is normal and clears once a consuming pod is scheduled.

Common Causes

  • No matching PV and no default StorageClass — nothing can satisfy the claim, so it never binds.
  • WaitForFirstConsumer (expected) — the PVC waits for a pod; harmless unless the pod is also stuck.
  • storageClassName mismatch — the PVC names a StorageClass that does not exist, or "" (which disables dynamic provisioning).
  • Access mode / size mismatch — for static PVs, no available PV matches the requested accessModes or capacity.
  • CSI driver / provisioner not running — the external-provisioner or CSI controller pod is down, so dynamic provisioning never happens.
  • Zone / topology mismatch — the PV exists in a zone where no schedulable node lives.

How to Reproduce the Error

Create a PVC referencing a StorageClass that does not exist:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: data
spec:
  accessModes: ["ReadWriteOnce"]
  storageClassName: "fast-ssd"   # no such StorageClass
  resources:
    requests:
      storage: 10Gi
kubectl apply -f pvc.yaml
kubectl get pvc data
NAME   STATUS    VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
data   Pending                                      fast-ssd       20s

The PVC never binds because no provisioner answers for fast-ssd.

Diagnostic Commands

# Is the claim bound? What StorageClass does it want?
kubectl get pvc

# Why is it stuck? (cause is in the Events)
kubectl describe pvc <PVC>

# Are there any matching PVs (for static provisioning)?
kubectl get pv

# Which StorageClasses exist, and is one the default?
kubectl get storageclass
# the default is annotated storageclass.kubernetes.io/is-default-class=true

# What binding mode does the class use?
kubectl get storageclass <SC> -o jsonpath='{.volumeBindingMode}{"\n"}'

# Is the CSI provisioner actually running?
kubectl get pods -n kube-system | grep -i 'csi\|provisioner'

The decisive output is kubectl get sc (is there a default?) combined with the describe pvc Events.

Step-by-Step Resolution

1. Distinguish “expected” from “broken”. If describe pvc shows waiting for first consumer to be created before binding, the class is WFFC and the PVC will bind once a pod mounts it. Check the pod:

kubectl get pods -o wide

If the pod is also Pending for another reason (e.g. FailedScheduling), fix that — the PVC is waiting correctly. If no pod references the PVC yet, create one.

2. Fix “no storage class is set”. no persistent volumes available for this claim and no storage class is set means the PVC has no storageClassName and there is no default class. Mark a StorageClass default, or set the class on the PVC:

kubectl patch storageclass standard \
  -p '{"metadata":{"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'

3. Fix a storageClassName mismatch. If the PVC names a class that does not exist, correct it to a real class. Note: storageClassName: "" explicitly disables dynamic provisioning and only binds to a pre-created PV — usually a mistake.

kubectl get sc
# recreate the PVC with a valid storageClassName (PVC class is immutable)

4. Fix access mode / size mismatch (static PVs). For manually created PVs, the PVC binds only to a PV whose accessModes include the requested mode and whose capacity is >= the request:

kubectl get pv -o custom-columns=NAME:.metadata.name,CAP:.spec.capacity.storage,MODES:.spec.accessModes,STATUS:.status.phase

Create or resize a PV that matches, e.g. an RWX 10Gi PV for an RWX 10Gi claim.

5. Fix a down provisioner. If the class is dynamic but nothing provisions, check the CSI controller:

kubectl get pods -n kube-system -l app=ebs-csi-controller
kubectl logs -n kube-system deploy/ebs-csi-controller -c csi-provisioner --tail=30

Restart or repair the provisioner; once it is healthy the Pending PVC provisions automatically.

6. Fix zone / topology mismatch. node(s) had volume node affinity conflict on the pod means the PV’s zone has no schedulable node. Use a WFFC StorageClass so the volume is provisioned in the zone the pod lands in, or add a node in the PV’s zone.

Prevention and Best Practices

  • Always have exactly one default StorageClass so PVCs without an explicit class still bind.
  • Prefer volumeBindingMode: WaitForFirstConsumer for zonal block storage so volumes are created in the pod’s zone.
  • Never set storageClassName: "" unless you intend manual static binding — it silently disables dynamic provisioning.
  • Monitor the CSI controller/provisioner pods; their outage turns every new PVC into an indefinite Pending.
  • Match accessModes to your storage backend (most block storage is RWO only; RWX needs a file/NFS backend).
  • Treat a Pending PVC as a fast alert — stateful pods cannot start until it binds. See Kubernetes & Helm guides.

Frequently Asked Questions

My PVC says waiting for first consumer — is that an error? No. That message means the StorageClass uses WaitForFirstConsumer, so binding is intentionally delayed until a pod mounts the PVC. It is only a problem if the consuming pod is itself stuck for another reason, or if no pod ever references the claim.

I have a matching PV but the PVC still will not bind. Why? Binding requires the PV’s accessModes to include the requested mode and capacity to be at least the requested size, and the PV must be Available (not already Released/Bound). A Released PV with a stale claimRef will not rebind until you clear the claimRef.

Can I change a PVC’s storageClassName after creation? No — it is immutable. Delete and recreate the PVC with the correct class. Back up any data first, since deleting a bound PVC may delete the PV depending on the reclaim policy.

Why does setting storageClassName: "" break dynamic provisioning? An empty string explicitly opts the PVC out of any StorageClass, so no provisioner is invoked and it can only bind to a manually pre-created PV. Omit the field entirely to fall back to the default class instead.

The PVC is bound but the pod still cannot mount it. Binding and mounting are separate. A bound PVC that fails to mount usually shows FailedMount/AttachVolume.Attach failed on the pod — check the CSI node plugin and the volume’s attachment, not the binding.

Free download · 368-page PDF

Download the Free 500-Prompt DevOps AI Toolkit

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.