1. Introduction
A PersistentVolumeClaim stuck in Pending means Kubernetes cannot bind it to a PersistentVolume. Any pod that references this PVC will also be stuck in Pending, waiting for storage that never becomes available. This is a blocking issue for stateful workloads — databases, message queues, and any application that needs persistent storage can't start until the PVC is bound.
PVC Pending is almost always one of: no matching PV exists, the StorageClass doesn't have a working provisioner, or access mode and storage class constraints prevent binding. Each has a distinct fix.
2. What PVC Pending Means
When a PVC is created, the Kubernetes control plane tries to find a PV that satisfies the claim's requirements: sufficient storage, matching access mode, matching StorageClass, and matching label selectors. If dynamic provisioning is configured, the provisioner creates a new PV automatically. If no PV matches and no provisioner runs, the PVC stays Pending.
3. Common Causes
- StorageClass references a provisioner that isn't deployed in the cluster
- No PV exists that satisfies the PVC's capacity, access mode, and storage class requirements
- The provisioner is deployed but failing (CSI driver crash, IAM permissions missing)
- Access mode mismatch — PVC requests
ReadWriteManybut StorageClass only supportsReadWriteOnce - The StorageClass doesn't exist or has a typo in the name
- For static provisioning: the PV's
claimRefpoints to a different PVC - Resource quota has been reached for storage in the namespace
4. Step-by-Step Diagnosis and Fix
Step 1: Read the PVC events
# Check PVC status and events
kubectl describe pvc <pvc-name> -n <namespace>
# Common messages:
# "no persistent volumes available for this claim and no storage class is set"
# "storageclass.storage.k8s.io "fast-ssd" not found"
# "waiting for a volume to be created, either by external provisioner "
# "waiting for first consumer to be created before binding" (WaitForFirstConsumer = normal)
# List all PVCs in the namespace
kubectl get pvc -n <namespace>
Step 2: Check if the StorageClass exists
# List available StorageClasses
kubectl get storageclass
# Check if there's a default StorageClass (marked with annotation)
kubectl get storageclass -o jsonpath='{range .items[*]}{.metadata.name}{" "}{.metadata.annotations.storageclass\.kubernetes\.io/is-default-class}{"
"}{end}'
# Describe the StorageClass used by the PVC
kubectl describe storageclass <storageclass-name>
# Look at: Provisioner, ReclaimPolicy, VolumeBindingMode
Step 3: Check the provisioner is running
"># For AWS EBS CSI driver:
kubectl get pods -n kube-system -l app=ebs-csi-controller
kubectl logs -n kube-system -l app=ebs-csi-controller -c ebs-plugin --tail=50
# For GCE PD CSI driver:
kubectl get pods -n kube-system -l app=gcp-compute-persistent-disk-csi-driver
# For NFS provisioner:
kubectl get pods -n <provisioner-namespace>
# If the provisioner is crashing, check its logs for IAM/auth errors
kubectl logs -n kube-system <csi-controller-pod> --tail=100
Step 4: Fix — Create a matching PV manually (static provisioning)
apiVersion: v1
kind: PersistentVolume
metadata:
name: my-pv
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: manual
hostPath:
path: "/mnt/data" # for testing; use proper backend in production
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
storageClassName: manual
Step 5: Fix — StorageClass with working provisioner
# For AWS EKS — ensure the EBS CSI driver is installed
kubectl get addon -n kube-system | grep ebs
# Install if missing:
eksctl create addon --name aws-ebs-csi-driver --cluster <cluster-name> --service-account-role-arn arn:aws:iam::<account>:role/AmazonEKS_EBS_CSI_DriverRole
# Use the correct StorageClass for AWS:
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: gp3
provisioner: ebs.csi.aws.com
volumeBindingMode: WaitForFirstConsumer
parameters:
type: gp3
encrypted: "true"
Step 6: Fix — Access mode issues
# Check what access modes the StorageClass supports
kubectl describe storageclass <name>
# EBS/block storage supports: ReadWriteOnce only
# NFS/EFS supports: ReadWriteMany, ReadOnlyMany, ReadWriteOnce
# If you need ReadWriteMany, use NFS/EFS/CephFS/Azure Files
# Fix access mode in PVC:
spec:
accessModes:
- ReadWriteOnce # change from ReadWriteMany if block storage
5. Verification Steps
# PVC should show Bound
kubectl get pvc -n <namespace>
# NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS
# my-pvc Bound pvc-abc123 10Gi RWO gp3
# Pods referencing the PVC should start
kubectl get pods -n <namespace> -l app=<your-app>
6. Common Mistakes
- Mistaking
WaitForFirstConsumerpending for an error — this is correct behaviour, the PVC binds when a pod is scheduled - Using a StorageClass name that doesn't exist — the PVC will stay Pending forever with a "not found" event
- Requesting
ReadWriteManyfrom a block device StorageClass — not supported; use a network filesystem instead - Not checking provisioner pod health — even if the StorageClass exists, a crashed provisioner won't create volumes
- Over-requesting storage — some provisioners have per-request maximums; a 1Ti request on a system limited to 16Gi per volume will fail
7. Prevention Tips
- Always define a default StorageClass so PVCs without an explicit class bind automatically
- Use
WaitForFirstConsumerin all multi-AZ StorageClasses to prevent zone mismatches - Validate StorageClass names in Helm charts and manifests as part of your CI/CD pipeline (
kubectl apply --dry-run=server) - Monitor provisioner pod health — a crashing CSI controller silently blocks all new PVC provisioning
- If PVC binds but the pod still can't mount, see Fix Kubernetes Persistent Volume Mount Failure — that guide covers multi-attach errors, AZ mismatches, and CSI driver failures
8. FAQ
The PVC was Bound and then went back to Pending. Is that possible?
Rare but possible. If the PV it was bound to was manually deleted, the PVC can return to Pending. Also, if the PVC's bound PV has a reclaimPolicy: Delete and the PV was released, it may be deleted — but the PVC will stay in a Lost state, not Pending. Check kubectl describe pvc for the current binding state and events.
How do I pre-provision PVs to avoid dynamic provisioning delays?
Create PVs statically with matching storageClassName, capacity, and accessModes, and leave their claimRef empty (the PVC will bind to the first matching available PV). For production, use dynamic provisioning with WaitForFirstConsumer — it's more flexible and avoids AZ mismatches that static pre-provisioning can introduce.
9. Summary
| PVC event message | Cause | Fix |
|---|---|---|
| storageclass not found | StorageClass doesn't exist | Create StorageClass or fix name typo in PVC |
| no volumes available | No matching PV, no provisioner | Install CSI driver; create PV manually |
| waiting for first consumer | WaitForFirstConsumer mode | Normal — PVC binds when pod is scheduled |
| provisioner not found | CSI controller not running | Deploy CSI driver DaemonSet and controller |
| access mode not supported | Wrong access mode for backend | Use RWO for block; use NFS/EFS for RWX |
Explore More in This Category
Explore more in this category: Kubernetes guides. Browse all DevOps Compass articles or jump to: Kubernetes, AWS, CI/CD, Containers, Monitoring, Networking.