🎉 DevOps Interview Prep Bundle is live — 1000+ Q&A across 20 topicsGet it →
All Articles

Kubernetes PVC Stuck in Pending? Here's How to Fix It (2026)

Step-by-step troubleshooting guide for PersistentVolumeClaims stuck in Pending state — covering StorageClass issues, provisioner problems, capacity limits, and access mode mismatches.

Shubham5 min read
Share:Tweet

PVC stuck in Pending is one of those errors that looks simple from the outside — "just give the pod its storage" — but can be caused by half a dozen different things, and the error messages are rarely specific enough to tell you which one.

Here is a systematic guide to diagnosing and fixing PVCs that refuse to bind.

Step 1: Check the PVC Status and Events

bash
kubectl describe pvc my-pvc -n your-namespace

The Events section is where all the useful information lives. Common messages you might see:

no persistent volumes available for this claim and no storage class is set
no persistent volumes available for this claim and its StorageClass does not exist
waiting for a volume to be created either by the external provisioner or manually

Each of these points to a different root cause.

Root Cause 1: No StorageClass Specified and No Default

The most common cause for freshly set-up clusters or claims migrated from another cluster.

bash
# Check if a default StorageClass exists
kubectl get storageclass

Look for the (default) annotation in the output:

NAME               PROVISIONER             RECLAIMPOLICY   VOLUMEBINDINGMODE      ALLOWVOLUMEEXPANSION
gp3 (default)      kubernetes.io/aws-ebs   Delete          WaitForFirstConsumer   true

If nothing is marked as default, any PVC without an explicit storageClassName will hang in Pending forever.

Fix option 1: Add a default annotation to an existing StorageClass:

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

Fix option 2: Specify the StorageClass explicitly in your PVC:

yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: my-pvc
spec:
  storageClassName: gp3  # Explicit — does not rely on default
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi

Root Cause 2: StorageClass Does Not Exist

bash
# Check what StorageClasses exist
kubectl get storageclass
 
# Your PVC is requesting one that is not listed
kubectl get pvc my-pvc -o jsonpath='{.spec.storageClassName}'

If the StorageClass name in the PVC does not match any existing StorageClass, you need to either create the StorageClass or fix the PVC to use an existing one.

This commonly happens when moving from one cluster to another — the new cluster has different provisioner names. For example, old cluster had gp2, new cluster has gp3.

Root Cause 3: Provisioner Not Running

The StorageClass exists but the CSI driver or external provisioner that backs it is not running.

bash
# Check the provisioner name
kubectl get storageclass gp3 -o jsonpath='{.provisioner}'
# Output: kubernetes.io/aws-ebs or ebs.csi.aws.com
 
# For EBS CSI driver on EKS — check if the driver pods are running
kubectl get pods -n kube-system | grep ebs-csi
 
# For generic CSI drivers
kubectl get pods -n kube-system | grep csi

If the provisioner pods are in CrashLoopBackOff or not present at all, the StorageClass cannot fulfill PVCs regardless of configuration.

On EKS, the EBS CSI driver must be installed as an add-on:

bash
aws eks create-addon \
  --cluster-name my-cluster \
  --addon-name aws-ebs-csi-driver \
  --service-account-role-arn arn:aws:iam::123456789:role/AmazonEKS_EBS_CSI_DriverRole

Root Cause 4: VolumeBindingMode = WaitForFirstConsumer

This is not a bug — it is expected behavior that confuses people.

bash
kubectl get storageclass gp3 -o jsonpath='{.volumeBindingMode}'
# Output: WaitForFirstConsumer

When VolumeBindingMode is WaitForFirstConsumer, the PVC will stay in Pending until a Pod that uses it is also pending or scheduled. This is intentional — it ensures the volume is created in the same availability zone as the pod.

If no pod is consuming this PVC yet, the PVC will stay in Pending. This is normal.

bash
# Check if there is a pod waiting on this PVC
kubectl get pod -o json | grep -A 5 "claimName: my-pvc"
 
# Confirm by checking pod events
kubectl describe pod my-pod
# Events should show: waiting for a volume to be provisioned

The fix is not to change the storage class — it is to deploy the pod that needs the PVC. Once a pod claims the PVC, provisioning will begin.

Root Cause 5: Access Mode Mismatch

If you have an existing PersistentVolume (PV) that you expect the PVC to bind to, access mode mismatch is a common cause of Pending.

bash
# Check PV access modes
kubectl get pv
 
# Check PVC access modes
kubectl get pvc my-pvc -o jsonpath='{.spec.accessModes}'

Common mismatches:

  • PVC requests ReadWriteMany but PV only supports ReadWriteOnce (EBS volumes can only be ReadWriteOnce)
  • PVC requests ReadWriteOnce but the PV was already created with a different mode

For EBS/EBS CSI: these disks only support ReadWriteOnce. If you need ReadWriteMany, you need EFS (for AWS) or equivalent shared storage.

Root Cause 6: Storage Capacity Exhausted

Less common but possible — the cluster has run out of provisioning capacity for the requested storage class in the requested zone.

bash
kubectl describe pvc my-pvc
# Look for: "no available capacity" or "failed to provision"

For EBS on EKS, this is rare but can happen in heavily used AZs. Moving the workload to a different AZ or using a different volume type usually resolves it.

Quick Diagnosis Flowchart

PVC stuck in Pending
    ↓
kubectl describe pvc → check Events
    ↓
"no storage class" → set default StorageClass or specify storageClassName
    ↓
"StorageClass does not exist" → fix storageClassName in PVC
    ↓
"waiting for volume" → check CSI driver pods are running
    ↓
VolumeBindingMode: WaitForFirstConsumer → deploy the consuming pod
    ↓
Access mode mismatch → fix accessModes to match PV / use appropriate storage type
    ↓
Still stuck → check provisioner logs: kubectl logs -n kube-system -l app=ebs-csi-controller

Checking Provisioner Logs

When nothing else explains it:

bash
# EBS CSI controller logs
kubectl logs -n kube-system \
  -l app=ebs-csi-controller \
  -c csi-provisioner \
  --tail=50
 
# General provisioner events
kubectl get events -n your-namespace --sort-by='.lastTimestamp' | tail -20

The provisioner logs often contain the actual error from the cloud API — permission denied, quota exceeded, invalid parameter — that explains exactly what is failing.


More Kubernetes storage troubleshooting? Check our Kubernetes OOMKilled fix guide and Kubernetes evicted pods disk pressure fix.

🔧

Today I Fixed

Short real fixes from production — posted daily

Browse fixes
Newsletter

Stay ahead of the curve

Get the latest DevOps, Kubernetes, AWS, and AI/ML guides delivered straight to your inbox. No spam — just practical engineering content.

Related Articles

Comments