Kubernetes PVC Pending

Causes and Fixes

A PersistentVolumeClaim (PVC) stuck in Pending status means Kubernetes cannot find or provision a PersistentVolume (PV) that satisfies the claim's requirements. The pod referencing this PVC will remain in Pending state until the volume is bound.

Symptoms

  • PVC status shows Pending in kubectl get pvc
  • Pods referencing the PVC are stuck in Pending state
  • Pod events show 'persistentvolumeclaim is not bound'
  • PVC events show 'no persistent volumes available for this claim'
  • Storage provisioner errors appear in controller manager or provisioner logs

Common Causes

1
No StorageClass available
The PVC requests a StorageClass that does not exist, or no default StorageClass is configured and the PVC does not specify one.
2
No matching PersistentVolume
For static provisioning, no pre-created PV matches the PVC's requirements for size, access mode, storage class, or labels.
3
Storage provisioner not functioning
The dynamic provisioner for the StorageClass is not running, misconfigured, or lacks permissions to create volumes in the underlying storage backend.
4
Capacity limits reached
The underlying storage system (cloud provider, NFS server, Ceph cluster) has reached its capacity or quota limits and cannot create new volumes.
5
Access mode not supported
The PVC requests an access mode (ReadWriteMany, ReadOnlyMany) that the storage backend does not support.
6
Zone or topology mismatch
In multi-zone clusters, the PVC is bound to a zone where no nodes are available, or the topology constraints cannot be satisfied.

Step-by-Step Troubleshooting

A Pending PVC blocks any pod that references it. Until the PVC transitions to Bound status, the pod cannot be scheduled. This guide walks through identifying why the PVC is stuck and how to resolve it.

1. Check the PVC Status and Events

Start with the PVC details to understand why binding is failing.

kubectl get pvc <pvc-name>

kubectl describe pvc <pvc-name>

The Events section is critical. Common event messages include:

  • no persistent volumes available for this claim and no storage class is set
  • storageclass.storage.k8s.io "<name>" not found
  • waiting for first consumer to be created before binding
  • provision failed: ...

2. Check the StorageClass

Verify the StorageClass referenced by the PVC exists and is properly configured.

# Check what StorageClass the PVC requests
kubectl get pvc <pvc-name> -o jsonpath='{.spec.storageClassName}'

# List available StorageClasses
kubectl get storageclass

# Check the default StorageClass
kubectl get storageclass -o custom-columns=NAME:.metadata.name,PROVISIONER:.provisioner,DEFAULT:.metadata.annotations.storageclass\.kubernetes\.io/is-default-class

If the PVC specifies a StorageClass that does not exist, either create the StorageClass or update the PVC (you will need to delete and recreate the PVC since storageClassName is immutable).

If the PVC has no storageClassName and there is no default StorageClass, no dynamic provisioning will occur.

3. Check the Volume Binding Mode

The StorageClass's volumeBindingMode affects when PVC binding occurs.

kubectl get storageclass <storage-class-name> -o jsonpath='{.volumeBindingMode}'
  • Immediate: The PVC is bound as soon as it is created (default)
  • WaitForFirstConsumer: The PVC remains Pending until a pod references it and gets scheduled

If the mode is WaitForFirstConsumer and you see waiting for first consumer, this is normal behavior. The PVC will bind once a pod that uses it is scheduled to a node.

# Check if there is a pod referencing this PVC
kubectl get pods --all-namespaces -o json | jq -r '.items[] | select(.spec.volumes[]?.persistentVolumeClaim.claimName=="<pvc-name>") | .metadata.name'

4. Check for Matching PersistentVolumes (Static Provisioning)

If you are using static provisioning (pre-created PVs), check if any PV matches the PVC.

# List available PVs
kubectl get pv

# Check PV details
kubectl get pv -o custom-columns=NAME:.metadata.name,CAPACITY:.spec.capacity.storage,ACCESS:.spec.accessModes,CLASS:.spec.storageClassName,STATUS:.status.phase

For a PV to match a PVC, it must:

  • Have equal or greater capacity than the PVC requests
  • Support the access modes the PVC requires
  • Have the same StorageClass (or both be empty)
  • Be in Available status (not already Bound)
  • Match any label selector the PVC specifies
# Compare PVC requirements with available PVs
kubectl get pvc <pvc-name> -o jsonpath='Size: {.spec.resources.requests.storage}, AccessModes: {.spec.accessModes}, Class: {.spec.storageClassName}'

5. Check the Storage Provisioner

For dynamic provisioning, the provisioner must be running and healthy.

# Find the provisioner for the StorageClass
kubectl get storageclass <storage-class-name> -o jsonpath='{.provisioner}'

# Common provisioners and where to find them:
# kubernetes.io/aws-ebs - built into the cloud controller manager
# kubernetes.io/gce-pd - built into the cloud controller manager
# ebs.csi.aws.com - AWS EBS CSI driver
# pd.csi.storage.gke.io - GCP PD CSI driver

# Check CSI driver pods
kubectl get pods --all-namespaces | grep csi

# Check CSI driver logs
kubectl logs -n kube-system <csi-controller-pod> --tail=100

If the provisioner is a CSI driver, verify it is running and check its logs for provision errors.

# Check CSI drivers
kubectl get csidrivers

# Check CSI nodes
kubectl get csinodes

6. Check Access Mode Compatibility

Not all storage backends support all access modes.

kubectl get pvc <pvc-name> -o jsonpath='{.spec.accessModes}'

Common limitations:

  • AWS EBS: Only supports ReadWriteOnce (single node)
  • GCP PD: Only supports ReadWriteOnce and ReadOnlyMany
  • Azure Disk: Only supports ReadWriteOnce

If you need ReadWriteMany (RWX), use a storage backend that supports it like NFS, CephFS, or Azure Files.

7. Check Storage Capacity and Quotas

The storage backend may be out of capacity.

# Check cloud provider volume limits (varies by provider)
# AWS: check EC2 volume limits
# GCP: check persistent disk quotas

# Check Kubernetes resource quotas
kubectl get resourcequota -n <namespace>
kubectl describe resourcequota -n <namespace> | grep -i storage

# Check CSIStorageCapacity objects (Kubernetes 1.24+)
kubectl get csistoragecapacity --all-namespaces

8. Check Topology Constraints

In multi-zone clusters, volumes may be provisioned in zones with no available nodes.

# Check node zones
kubectl get nodes -L topology.kubernetes.io/zone

# For WaitForFirstConsumer, check which zone the pod would be scheduled to
kubectl describe pod <pending-pod> | grep -i "topology\|zone"

If using WaitForFirstConsumer, ensure nodes exist in the zone where provisioning will occur. If using Immediate binding, the volume may be provisioned in a zone with no nodes that can access it.

9. Delete and Recreate the PVC

If the PVC was created with wrong parameters (wrong StorageClass, wrong size, wrong access mode), you need to delete and recreate it since these fields are immutable.

# Delete the PVC (ensure no pod is using it first)
kubectl delete pvc <pvc-name>

# Recreate with correct parameters
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: <pvc-name>
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: <correct-class>
  resources:
    requests:
      storage: 10Gi
EOF

10. Verify the PVC Is Bound

After resolving the issue, confirm the PVC transitions to Bound.

# Watch the PVC status
kubectl get pvc <pvc-name> -w

# Check the bound PV
kubectl get pvc <pvc-name> -o jsonpath='Status: {.status.phase}, Volume: {.spec.volumeName}'

# Verify the pod can now start
kubectl get pods | grep <pod-name>

A Bound PVC means a PV has been provisioned or matched and the pod can proceed with scheduling and starting. If the PVC remains Pending, review the events again for updated error messages that may reveal additional issues.

How to Explain This in an Interview

I would explain the PV/PVC binding model: PVCs are requests for storage, PVs are the actual storage resources, and the binding happens based on matching criteria including size, access modes, StorageClass, and selectors. I'd discuss static versus dynamic provisioning, the role of the StorageClass and its provisioner, and the lifecycle of a PVC from Pending to Bound to in-use. I'd cover volume topology awareness (WaitForFirstConsumer binding mode) and why it exists — to prevent volumes from being provisioned in zones where no pod can use them. For debugging, I'd check StorageClass existence, provisioner health, and the PVC events for specific failure reasons.

Prevention

  • Always verify the StorageClass exists before creating PVCs
  • Use WaitForFirstConsumer binding mode for topology-aware provisioning
  • Monitor storage capacity and set alerts before limits are reached
  • Test storage provisioning in non-production environments first
  • Document supported access modes for each StorageClass

Related Errors