Home/Infrastructure/Kubernetes PVC Stuck in Pending: 6 Causes and a 5-Minute Diagnostic Guide
InfrastructureKubernetesPVC pending

Kubernetes PVC Stuck in Pending: 6 Causes and a 5-Minute Diagnostic Guide

Is kubectl get pvc stuck on Pending and your Pod frozen at ContainerCreating? Pin down which of the six event-specific causes you have—no persistent volumes available, waiting for first consumer, and more—with a decision table and copy-past

Kubernetes PVC Stuck in Pending: 6 Causes and a 5-Minute Diagnostic Guide
Contents
  1. 1-Minute First Pass: The Four Commands to Run First
  2. Six-Cause Decision Table (the core of this post)
  3. Where scope trips people up
  4. Static vs. Dynamic Provisioning — the underlying model
  5. Wrap-up: 5-minute diagnostic checklist
  6. References: official docs
  7. FAQ

When a Kubernetes PVC Won't Leave Pending — 6 Causes, 5-Minute Diagnostic Guide

You rolled out a deployment, but the Pod is stuck forever in ContainerCreating. You run kubectl get pvc and see this:

Bash
$ kubectl get pvc
NAME        STATUS    VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
data-pvc    Pending                                      standard       8m

STATUS=Pending has not cleared for several minutes. A lot of people immediately suspect the scheduler—"are we short on node resources?"—but that's a different problem.

⚠️ The previous post, "Kubernetes Pod Pending," covered cases where the scheduler cannot place a Pod on a node—CPU/memory, taints, insufficient nodes, and so on. This post is a storage-only issue: the PV never binds to the PVC, so the volume never attaches. If the real reason the Pod is Pending is the PVC, staring at the Pod will never give you the answer.

This article is a playbook that maps event text → cause → fix command in three steps, so you can pin down which of the six causes matches the message in front of you within five minutes.

1-Minute First Pass: The Four Commands to Run First

Don't overthink it—run these four as-is right now. The cause is often printed right in the Events section of the first command.

Bash
# ① — Events section as-is
kubectl describe pvc <pvc-name>

# ② PV ,
kubectl get pv

# ③ StorageClass list default check
kubectl get sc

# ④ when
kubectl get events --sort-by=.metadata.creationTimestamp

Branch on the single line printed under Events at the bottom of describe pvc.

Event textJump to cause
storageclass.storage.k8s.io "xxx" not found① StorageClass name typo
(no events + empty storageClassName)② No default StorageClass
waiting for first consumer to be created before binding③ WaitForFirstConsumer (expected)
no persistent volumes available for this claim④ accessModes/capacity mismatch
failed to provision volume with StorageClass⑤ CSI driver not installed
(PVC is Bound but the Pod is Pending, with volume node affinity conflict in the Pod events)⑥ Topology/zone mismatch

Six-Cause Decision Table (the core of this post)

#Event / symptom textCauseCheck commandFix command
①storageclass.storage.k8s.io "fast" not foundstorageClassName typo / class does not existkubectl get scFix storageClassName in the PVC manifest to the real name and re-apply
②Pending with no events, storageClassName omittedNo default StorageClassConfirm (default) in kubectl get sckubectl patch storageclass <name> -p '{"metadata":{"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'
③waiting for first consumer to be created before bindingWaitForFirstConsumer (expected behavior)kubectl get sc <name> -o yaml | grep volumeBindingModeBinding happens once you actually create the Pod that mounts the PVC
④no persistent volumes available for this claimaccessModes/capacity mismatch (static PV)kubectl get pv -o wideFix PVC accessModes, or use RWX-capable storage (NFS/CephFS)
⑤failed to provision volume with StorageClass or infinite PendingCSI driver/provisioner not installedkubectl get csidrivers, kubectl get pods -n kube-system | grep csiInstall the CSI driver (EBS/GCE PD, etc.)
⑥PVC is Bound; the Pod events show volume node affinity conflictThe PV was created in a different zone from the nodes the Pod can run on (usually with Immediate binding)kubectl describe pod <pod>, nodeAffinity in kubectl get pv <pv> -o yaml, the node's topology.kubernetes.io/zone labelCreate the PV in the same zone, or use WaitForFirstConsumer for topology-aware provisioning

① StorageClass name typo

This is the most common case and the fastest to fix. Confirm the real name with kubectl get sc and correct the manifest. Managed clusters use different defaults—EKS gp2/gp3, GKE standard-rwo, AKS default—so if you copy-pasted YAML from another cluster, start here.

② No default StorageClass

A PVC that omits storageClassName entirely uses the cluster's default StorageClass. If there is no default at all, it stays Pending forever.

Bash
kubectl get sc
# NAME (default) set
kubectl patch storageclass standard \
  -p '{"metadata":{"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'

③ WaitForFirstConsumer — the most commonly misunderstood case

CODE
Events:
  Normal  WaitForFirstConsumer  ...  waiting for first consumer to be created before binding

This is not a failure; it is expected behavior. A StorageClass with volumeBindingMode: WaitForFirstConsumer deliberately delays binding until a Pod that uses the PVC is actually scheduled. That prevents the PV and Pod from landing in different zones on a multi-AZ cluster. The fix is to create the Pod that mounts the PVC. Leaving a PVC sitting by itself and wondering why it never binds is extremely common.

④ accessModes mismatch (RWO vs RWX)

no persistent volumes available for this claim appears in static provisioning when no PV matches. A common cause is a PVC requesting ReadWriteMany(RWX) while block storage such as EBS or GCE PD does not support RWX. If you truly need RWX, use file storage such as NFS, CephFS, or EFS.

Read accessModes like this. Kubernetes docs: Persistent Volumes

accessModeMeaningRelation to PVC Pending
ReadWriteOnce (RWO)Read-write mount on one node. Multiple Pods on the same node can share itThe default for block storage (EBS, GCE PD, etc.). If a Pod on another node tries to use the same volume, the PVC is Bound and the Pod gets a Multi-Attach error (not a PVC Pending cause)
ReadWriteOncePod (RWOP)Read-write mount by one Pod only (CSI volumes only, GA in Kubernetes 1.29)If the driver doesn't support it, provisioning fails and the PVC stays Pending
ReadOnlyMany (ROX)Read-only mount on many nodesIf the backend doesn't support it, matching or provisioning fails
ReadWriteMany (RWX)Read-write mount on many nodesBlock storage usually doesn't support it → no persistent volumes available for static PVs, a provisioning failure for dynamic ones

⑤ CSI driver not installed

This has become one of the most frequent causes. After CSI migration of in-tree volume plugins completed, EBS, GCE PD, and similar volumes require a separately installed CSI driver for dynamic provisioning.

Bash
kubectl get csidrivers
kubectl get pods -n kube-system | grep csi
# EKS : aws-ebs-csi-driver

If the driver is installed and the PVC is still Pending, check that the StorageClass provisioner matches the real driver name. If a StorageClass copied from another cluster names a provisioner that doesn't exist, nobody ever creates the PV.

Bash
kubectl get sc <sc-name> -o jsonpath='{.provisioner}{"\n"}'   # e.g. ebs.csi.aws.com
kubectl get csidrivers                                          # a driver with the same name must exist
# The reason for a provisioning failure is in the CSI controller's csi-provisioner container log (EBS CSI example)
kubectl logs -n kube-system deploy/ebs-csi-controller -c csi-provisioner --tail=50

If describe pvc shows ProvisioningFailed with a cloud API error in the message, suspect one of these two.

  • Permission errors (UnauthorizedOperation, AccessDenied, 403, etc.): the problem is not the RBAC of the user who created the PVC but the CSI controller's permission to call the cloud API. On EKS, check that the EBS CSI controller's ServiceAccount has IAM permissions such as AmazonEBSCSIDriverPolicy attached through IRSA or EKS Pod Identity.
  • Parameter errors (InvalidParameter, etc.): the StorageClass parameters don't match the driver docs. For EBS, for example, check the allowed values and ranges of type, iops, throughput, and encrypted in the driver docs and fix them. A StorageClass's parameters can't be changed after creation, so create a new StorageClass.

⑥ Capacity and zone topology mismatch

Here the PVC is already Bound, and the symptom shows up in the Pod events (volume node affinity conflict). Compare the PV's zone from kubectl get pv -o wide with the node's topology.kubernetes.io/zone label. If the PV is in ap-northeast-2a and the Pod is scheduled to 2c, it will never attach. Topology-aware provisioning (WaitForFirstConsumer) is the right answer.

After each change, watch in real time with:

Bash
kubectl get pvc -w

Where scope trips people up

A StorageClass is always a cluster-scoped resource. You cannot create one per namespace, and a PVC in any namespace can reference the same StorageClass as long as the name matches. So a PVC never stays Pending because "the PVC and the StorageClass are in different namespaces." Kubernetes docs: Storage Classes

Bash
# NAMESPACED shows false for cluster-scoped resources
kubectl api-resources | grep -i storageclass

The places where scope really does trip people up are these two:

  • A PVC is a namespaced resource. Pass the PVC's namespace with -n to kubectl get pvc and kubectl describe pvc, and the Pod that uses the PVC must be in the same namespace.
  • A static PV (cluster-scoped) reserved for a specific PVC: if the namespace and name in the PV's spec.claimRef do not match the actual PVC, it will not bind and the PVC stays Pending.

To limit how much of a given StorageClass each namespace can use, use a ResourceQuota entry such as <storage-class-name>.storageclass.storage.k8s.io/requests.storage. When that limit is exceeded, the PVC does not go Pending; it is rejected at creation time (exceeded quota). Kubernetes docs: Resource Quotas

Static vs. Dynamic Provisioning — the underlying model

  • Static: An admin-created PV binds to a PVC only when capacity/accessModes/storageClassName/selector all match. Any mismatch yields no persistent volumes available.
  • Dynamic: The StorageClass plus a provisioner (CSI) automatically creates a PV that fits the PVC. Without a provisioner, it stays Pending.

Immediate binds as soon as the PVC is created (fine for a single zone). WaitForFirstConsumer delays until the Pod is scheduled (the recommended default for multi-AZ).

Minimal YAML trio for reproduction

YAML
# 1) StorageClass
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: demo-sc
provisioner: ebs.csi.aws.com
volumeBindingMode: WaitForFirstConsumer
---
# 2) PVC
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: data-pvc
spec:
  accessModes: ["ReadWriteOnce"]
  storageClassName: demo-sc
  resources:
    requests:
      storage: 5Gi
---
# 3) PVC Pod ( ③ )
apiVersion: v1
kind: Pod
metadata:
  name: app
spec:
  containers:
  - name: app
    image: nginx
    volumeMounts:
    - { name: data, mountPath: /data }
  volumes:
  - name: data
    persistentVolumeClaim:
      claimName: data-pvc

A note from the field

In practice, more than half of PVC Pending cases on multi-AZ EKS/GKE are #③ (expected behavior, but nobody created a Pod) and #⑤ (CSI not installed). When you get a new cluster, make a habit of checking kubectl get sc and kubectl get csidrivers before you even launch a Pod—you'll skip this entire class of wasted hours.

Wrap-up: 5-minute diagnostic checklist

  1. kubectl describe pvc → read the Events line
  2. kubectl get sc → class exists / is default
  3. Check binding mode → if WaitForFirstConsumer, create the Pod first
  4. Confirm accessModes / capacity / zone match
  5. Confirm the driver is installed with kubectl get csidrivers

Prevention tips: Always set a default StorageClass, always specify storageClassName in the manifest, and if you need RWX, pick storage that supports it from the start.

Next post (part 12) preview: When the PVC is Bound but the mount still hangs — fixing Unable to attach or mount volumes / volume node affinity conflict / MountVolume.SetUp failed.

References: official docs

The primary source for the behavior, settings, and errors covered here is the official documentation below. Check it for version-specific options and exact semantics.

FAQ

Q. waiting for first consumer showed up. Is it broken? A. No. That is expected behavior. Binding happens when you actually create and schedule a Pod that mounts the PVC.

Q. Why do I see no persistent volumes available for this claim? A. None of the static PVs match the PVC's accessModes/capacity/storageClassName. It is especially common when you request RWX against block storage.

Q. PVC won't bind on EKS. Is the cluster broken? A. Most of the time the aws-ebs-csi-driver add-on is missing. Check the driver first with kubectl get csidrivers.

Verification
✦ ✦ ✦
Sources and verification

Sources and checks vary by article. Only articles with a recorded check date, scope and sources below were checked, and only within that scope. For other articles, verify against the current docs for your version before applying anything, and email us about errors. Editorial policy · email us

This article has no recorded human check (date and scope) yet.

Published · ·Updated ·
Topic-related official docs (auto-linked · not per-claim sources)Kubernetes documentation ↗pgvector repository ↗

Comments

Be the first to comment.