Kubernetes persistent volume claim stuck in Pending
Fixes Kubernetes PersistentVolumeClaims stuck in Pending. Use when a PVC never binds, pods wait on unbound claims, or storage provisioning fails silently. Triggers: PVC Pending, no persistent volumes available, storageclass not found, provisioner errors, waiting for first consumer. Not for: pods failing to mount an already-bound PVC, full volumes (disk space), data corruption on volumes.
TL;DR
A PVC stuck in Pending means no PersistentVolume matched it: check the storage class exists, the provisioner is healthy, and the access mode and size are satisfiable. Most cases are a typo in the storage class name, a missing provisioner, or WaitForFirstConsumer confusion. Fix the binding inputs and the PVC binds.
The error
NAME STATUS VOLUME CAPACITY STORAGECLASS AGE
mydata Pending 10mEvents: no persistent volumes available for this claim and no storage class is set, or waiting for a volume to be created.
Use this when
kubectl get pvcshows Pending for a long time- pods are stuck waiting on unbound immediate claims
- a new storage class was just introduced
- provisioning worked before and stopped
Not for
- mount failures on bound PVCs (kubelet/volume attach issues)
- volumes filling up (capacity, not binding)
- data loss or corruption on existing volumes
Steps
- Read the PVC's events for the provisioner's complaint:
kubectl describe pvc [pvc-name] -n [namespace]Expected: Events name the problem: storageclass.storage.k8s.io "fast" not found, no persistent volumes available, or provisioner errors.
- Verify the storage class exists and has a working provisioner:
kubectl get storageclass
kubectl get pods -n kube-system -l app=[provisioner-name]Expected: the class named in the PVC exists, and its provisioner pods are Running. A typo in storageClassName is the number one cause.
- Check the binding mode, because WaitForFirstConsumer looks stuck:
kubectl get storageclass [class] -o jsonpath='{.volumeBindingMode}'Expected: WaitForFirstConsumer means the PVC stays Pending until a pod actually uses it, which is normal, not broken. Immediate should bind right away.
- Match access modes and size against what the provisioner offers:
kubectl get pvc [pvc-name] -n [namespace] -o jsonpath='{.spec.accessModes}{.spec.resources.requests.storage}'Expected: ReadWriteOnce is widely supported; ReadWriteMany needs a provisioner that offers it (NFS, cloud filestore). Absurd sizes or unsupported modes never bind.
- For static provisioning, check a matching PV exists:
kubectl get pvExpected: an Available PV with matching capacity, access mode, and storage class (or no class if the PVC sets none). Claims only bind to PVs whose class matches theirs.
- Fix and re-check:
kubectl apply -f pvc.yaml
kubectl get pvc [pvc-name] -n [namespace] -wExpected: STATUS flips to Bound and VOLUME gets a name. Note: storageClassName is immutable on a PVC; if its wrong, delete and recreate the PVC (data loss only if something was written, which it wasnt, since it never bound).
Variant: Pending with provisioning failed and cloud errors
The provisioner lacks IAM permission or the zone/region has no capacity. Check the provisioner logs and cloud quotas.
Variant: worked yesterday, Pending today, nothing changed
The provisioner's backend ran out of quota or the driver pods died. Check provisioner pod health and cloud-side limits first.
Variant: PVC binds but the pod still wont start
Now its a mount/attach problem, not binding: check node volume limits (cloud providers cap volumes per node) and kubectl describe pod for FailedMount events.
Why it happens
Binding is a matchmaking pass: the PVC's class, size, and access mode must match an available PV or a provisioner able to create one. WaitForFirstConsumer deliberately delays binding until scheduling, which looks like stuck but isnt. Everything else is a mismatch in the inputs or a sick provisioner.
Edge cases
- Deleting a PVC with
persistentVolumeReclaimPolicy: Deletedestroys the volume and data; Retain keeps it but requires manual cleanup. - Some provisioners cant expand volumes; set
allowVolumeExpansion: trueon the class if you need growth. - Cross-zone: a volume provisioned in zone A cant attach to a pod scheduled in zone B; topology constraints matter.
- Default storage class annotation: if none is marked default and the PVC names no class, it never provisions.
Provenance
Resolved from the public thread: https://vectle.com/posts/pst3xTq1VEIxEHJdkID1Wsbw
Maintainer review
No maintainer verification is recorded for this version.
This records the version a maintainer checked. It does not assert that the version is the latest upstream release.