Troubleshooting Kubernetes PVC Stuck in Pending on Debian 12 Bookworm
Resolve Kubernetes Persistent Volume Claims (PVCs) stuck in pending status on Debian 12. Diagnose common causes like missing PVs, misconfigured StorageClasses, and provisioner issues.
Resolve Kubernetes Persistent Volume Claims (PVCs) stuck in pending status on Debian 12. Diagnose common causes like missing PVs, misconfigured StorageClasses, and provisioner issues.
A Kubernetes Persistent Volume Claim (PVC) stuck in a "Pending" state is a common yet critical issue that prevents applications from starting or functioning correctly, as they cannot mount their required storage. This guide provides a highly technical, step-by-step approach to diagnose and resolve PVCs perpetually pending on a Kubernetes cluster running on Debian 12 Bookworm.
Symptom & Error Signature
When a PVC is stuck in pending, applications requiring that storage will fail to start or remain in a "ContainerCreating" or "Pending" state themselves. You will typically observe the following when inspecting the PVC:
kubectl get pvc -n <namespace>
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
my-app-pvc Pending standard-ssd 5m
another-pvc Pending nfs-storage 2m
A more detailed inspection using kubectl describe pvc will often reveal the underlying reason in the "Events" section:
kubectl describe pvc my-app-pvc -n my-app-namespace
Name: my-app-pvc
Namespace: my-app-namespace
StorageClass: standard-ssd
Status: Pending
Volume:
Labels: <none>
Annotations: volume.beta.kubernetes.io/storage-class: standard-ssd
volume.beta.kubernetes.io/storage-provisioner:
Finalizers: [kubernetes.io/pvc-protection]
Capacity: 2Gi
Access Modes:
Volume Mode: Filesystem
DataSource: <unset>
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning ProvisioningFailed 14s (x3 over 4m3s) persistentvolume-controller failed to provision volume with StorageClass "standard-ssd": storageclass.storage.k8s.io "standard-ssd" not found
Warning FailedBinding 14s (x3 over 4m3s) persistentvolume-controller no persistent volumes available for this claim and no storage class is set
Or, if a StorageClass is specified but a provisioner is missing or misconfigured:
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning ProvisioningFailed 2m persistentvolume-controller failed to provision volume with StorageClass "nfs-storage": failed to get provisioner for StorageClass "nfs-storage": provisioner "cluster.local/nfs-subdir-external-provisioner" not found
Warning FailedBinding 2m persistentvolume-controller no persistent volumes available for this claim and no storage class is set
Another common message, especially for dynamically provisioned volumes without immediate consumers:
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal WaitForFirstConsumer 2m persistentvolume-controller waiting for first consumer to be created before binding
Root Cause Analysis
A Kubernetes PVC enters a "Pending" state when the Kubernetes control plane cannot find or provision a suitable PersistentVolume (PV) to satisfy the claim's requirements. The underlying reasons are typically one or a combination of the following:
- Missing or Misconfigured StorageClass: The PVC requests a
storageClassNamethat either does not exist, or the specified StorageClass is improperly configured (e.g., incorrectprovisionername, missing parameters). - No Matching PersistentVolume (PV) for Static Provisioning: If using static provisioning (i.e., you manually create PVs), no existing PV matches the PVC's criteria (capacity, access modes, volume mode, StorageClass name, labels, selectors). All suitable PVs might already be bound.
- Dynamic Provisioner Issues:
- Provisioner Not Deployed: The specified
provisioner(e.g.,nfs-subdir-external-provisioner,local-path-provisioner, a CSI driver) is not running or correctly installed in the cluster. - Provisioner Misconfiguration: The provisioner's deployment, RBAC, or configuration parameters (e.g., NFS server IP, share path, cloud provider credentials) are incorrect, preventing it from creating volumes.
- Underlying Storage System Problems: The external storage backend (NFS server, iSCSI target, cloud storage service) is inaccessible, out of capacity, or experiencing issues.
- Provisioner Not Deployed: The specified
- Insufficient Storage Capacity: Even if a StorageClass or PVs exist, there might not be enough available capacity on the underlying storage to fulfill the PVC's request.
- Access Mode Mismatch: The PVC's requested
accessModes(e.g.,ReadWriteOnce,ReadOnlyMany,ReadWriteMany) do not match any available PV or the capabilities of the storage provisioner. WaitForFirstConsumerPolicy: If the StorageClass (or the PVC's annotation) usesvolumeBindingMode: WaitForFirstConsumer, the PVC will intentionally remain pending until a Pod is scheduled to use it. This is a normal behavior, but can be confusing if not understood.- RBAC/Permissions: The Kubernetes ServiceAccount used by the provisioner or controller-manager might lack the necessary permissions to interact with the underlying storage system or Kubernetes API.
- Control Plane Health: While less common, an unhealthy
kube-controller-manager(responsible for PVC-PV binding) could lead to this issue.
Step-by-Step Resolution
Follow these steps to diagnose and resolve a PVC stuck in pending status.
1. Inspect the Persistent Volume Claim (PVC) Events
This is always the first step. The events in kubectl describe pvc usually point directly to the problem.
kubectl describe pvc <pvc-name> -n <namespace>
Carefully examine the Events section. Look for Warning messages like "failed to provision volume with StorageClass…", "no persistent volumes available…", or "provisioner '…' not found".
2. Verify StorageClass Configuration
If the PVC specifies a storageClassName, ensure it exists and is correctly configured.
kubectl get sc
If the StorageClass is missing, you'll need to create it. If it exists, describe it:
kubectl describe sc <storageclass-name>
Common issues:
- Missing StorageClass: If
kubectl get scdoes not list thestorageClassNamespecified in your PVC, create it. - Incorrect
provisionername: Theprovisionerfield in the StorageClass must exactly match the name the deployed provisioner registers itself with. - Missing or incorrect
parameters: Cloud-specific StorageClasses or custom CSI drivers often require specific parameters (e.g.,type,zone,fsType). ReclaimPolicyorvolumeBindingModemisconfiguration: While not direct causes for "Pending", they are important for volume lifecycle.WaitForFirstConsumerwill keep PVCs pending until a Pod uses them.
Ensure the
provisionername in your StorageClass YAML exactly matches the string your actual storage provisioner (e.g., NFS Subdir External Provisioner, AWS EBS CSI driver) registers with Kubernetes. A mismatch here is a frequent cause ofProvisioningFailed.
Example of a simple NFS-backed StorageClass:
# nfs-storageclass.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: nfs-storage
provisioner: cluster.local/nfs-subdir-external-provisioner # This must match your provisioner deployment
parameters:
archiveOnDelete: "false" # Set to "true" to archive data instead of deleting
reclaimPolicy: Delete
volumeBindingMode: Immediate # Or WaitForFirstConsumer
Apply with:
kubectl apply -f nfs-storageclass.yaml
3. Check Dynamic Provisioner Health and Logs
If your StorageClass uses dynamic provisioning, confirm the provisioner pod is running and check its logs.
Identify the provisioner pod: Find the
provisionerstring fromkubectl describe sc <storageclass-name>. Then, search for pods that match:kubectl get pods -A | grep -i <provisioner-substring> # Example for NFS: kubectl get pods -A | grep -i nfs-subdir-external-provisioner # Example for local-path: kubectl get pods -A | grep -i local-path-provisionerNote the namespace and pod name.
Check pod status:
kubectl get pod <provisioner-pod-name> -n <provisioner-namespace>Ensure it's
RunningandReady. If not, investigate why (e.g., ImagePullBackOff, CrashLoopBackOff) by describing the pod:kubectl describe pod <provisioner-pod-name> -n <provisioner-namespace>Examine provisioner logs:
kubectl logs <provisioner-pod-name> -n <provisioner-namespace> --tail=50Look for error messages related to volume creation, connection failures to the underlying storage, or permission issues.
If using
local-path-provisioner, ensure the host path/var/lib/rancher/k3s/storage(or/mnt/dataif manually configured) exists on your worker nodes and has appropriate permissions (e.g.,0777).
4. Verify Underlying Storage System
If the provisioner logs indicate issues with the backend storage, investigate that system directly.
NFS:
- Verify the NFS server is running and accessible from your Kubernetes nodes.
- Check
showmount -e <nfs-server-ip>from a node to confirm exports. - Check firewall rules on both the Kubernetes nodes and the NFS server.
- Ensure the NFS share has enough free space.
- Try manually mounting the NFS share from a node to test connectivity and permissions:
# On a Kubernetes worker node (Debian 12) sudo apt update && sudo apt install -y nfs-common sudo mkdir -p /mnt/nfs_test sudo mount <nfs-server-ip>:/<path/to/share> /mnt/nfs_test df -h /mnt/nfs_test sudo umount /mnt/nfs_test
iSCSI:
- Verify the iSCSI target is running and configured correctly.
- Check
iscsiadm -m discovery -t sendtargets -p <iscsi-target-ip>from a node. - Ensure appropriate firewall rules are in place.
Cloud Providers (AWS EBS, Azure Disk, GCP Persistent Disk):
- Check your cloud provider console for disk quotas, region availability, and service health.
- Ensure the Kubernetes cluster's IAM roles/Service Accounts have the necessary permissions to create and manage disk volumes.
5. Check for Existing Persistent Volumes (PVs) (Static Provisioning)
If you are using static provisioning or dynamic provisioning has failed to create a PV, inspect the existing PVs.
kubectl get pv
Look for PVs with Status Available. Then, describe them:
kubectl describe pv <pv-name>
Compare the PV's properties (Capacity, Access Modes, StorageClass, Volume Mode, Node Affinity if local) against the PVC's requirements.
For a PVC to bind to an existing PV, the following must match:
accessModes(e.g., ReadWriteOnce, ReadWriteMany)capacity(PV must be equal or larger than PVC request)storageClassName(must be identical, or both empty)volumeMode(Filesystem or Block)
If no matching PV exists and dynamic provisioning is not used or failing, you may need to manually create a PV.
Example of a static PV definition for an NFS share:
# nfs-pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: my-nfs-pv
spec:
capacity:
storage: 5Gi # Must be >= PVC requested capacity
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
storageClassName: nfs-storage # Must match the StorageClass used by the PVC
mountOptions:
- hard
- nfsvers=4.1
nfs:
path: /path/on/nfs/server/my-app-data # The actual path on your NFS server
server: 192.168.1.100 # Your NFS server IP address
Apply with:
kubectl apply -f nfs-pv.yaml
6. Address WaitForFirstConsumer Policy
If kubectl describe pvc shows Events: Normal WaitForFirstConsumer, this indicates the PVC is intentionally pending until a Pod requests it. This is normal.
# storageclass-wait-for-first-consumer.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: delayed-binding
provisioner: example.com/my-provisioner
volumeBindingMode: WaitForFirstConsumer # The key setting
reclaimPolicy: Delete
To resolve this "pending" status, you simply need to deploy the Pod that uses this PVC. The binding will happen automatically when the scheduler attempts to place the Pod.
# my-app-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 1
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-app-container
image: nginx:latest
ports:
- containerPort: 80
volumeMounts:
- name: my-storage
mountPath: /usr/share/nginx/html
volumes:
- name: my-storage
persistentVolumeClaim:
claimName: my-app-pvc # This PVC will trigger the binding
7. Check for RBAC Issues
Ensure your storage provisioner's ServiceAccount has the necessary permissions. This usually involves ClusterRole and ClusterRoleBinding resources. Refer to the specific provisioner's documentation for required RBAC.
Example (common for many provisioners):
# rbac.yaml for a generic provisioner
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-provisioner-sa
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: my-provisioner-runner
rules:
- apiGroups: [""]
resources: ["persistentvolumes"]
verbs: ["get", "list", "watch", "create", "delete"]
- apiGroups: [""]
resources: ["persistentvolumeclaims"]
verbs: ["get", "list", "watch", "update"]
- apiGroups: ["storage.k8s.io"]
resources: ["storageclasses"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["events"]
verbs: ["create", "update", "patch"]
- apiGroups: [""]
resources: ["endpoints"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
- apiGroups: [""]
resources: ["nodes"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: my-provisioner-runner-binding
subjects:
- kind: ServiceAccount
name: my-provisioner-sa
namespace: kube-system
roleRef:
kind: ClusterRole
name: my-provisioner-runner
apiGroup: rbac.authorization.k8s.io
8. Restart Affected Components
After making configuration changes, it's often beneficial to restart the affected components.
Delete and recreate the PVC: If the initial binding attempt failed due to a misconfiguration, sometimes deleting and recreating the PVC can trigger a fresh binding attempt.
kubectl delete pvc <pvc-name> -n <namespace> # Recreate from your YAML file kubectl apply -f my-pvc.yamlRestart the provisioner pod: If you changed the provisioner's configuration, delete the pod to force a restart.
kubectl delete pod <provisioner-pod-name> -n <provisioner-namespace>Restart
kube-controller-manager(Advanced/Last Resort): For self-hosted Kubernetes or if running in a VM/bare metal, you might need to restart thekube-controller-managerprocess on your control plane node. This is a powerful step and should only be done if other methods fail and you suspect a controller-manager issue. Forkubeadmdeployments on Debian 12,kube-controller-managerruns as a static pod. You would typically restart the kubelet service which manages static pods:# On the Kubernetes control plane node sudo systemctl restart kubeletRestarting
kube-controller-managerorkubeleton a control plane node can temporarily impact cluster stability. Perform this during a maintenance window.
By systematically going through these steps, you should be able to identify and resolve the root cause of a Kubernetes PVC stuck in pending status on your Debian 12 Bookworm cluster.