Troubleshooting storage persistence is the skill of diagnosing why a container lost its data or why a pod cannot start because a storage request cannot be fulfilled. For CKA candidates, this topic carries heavy weight because the exam tests your ability to read error messages, inspect object statuses, and fix binding failures – all under time pressure.
Jump to a section
A simple way to picture Troubleshooting Storage Persistence
You buy a 500-litre fish tank for your living room. You fill it with water and add 4 goldfish. Everything is fine. Then you notice the tank is leaking. You have a PersistentVolumeClaim – a request for 500 litres of water. The PersistentVolume is the actual 500-litre tank. The StorageClass is the shop that sells you the tank. The tank leaks because the glass has a crack – a backend issue like a faulty disk. You check the PVC to see if the water level matches the request. You check the PV to see if the tank really holds 500 litres. You check the StorageClass to see if the shop delivered the right type of tank (e.g., glass vs acrylic – like SSD vs HDD). When a pod cannot start, you look at the PVC binding status. If the PVC is pending, either no PV matches the requested size, or the StorageClass cannot provision one. You might now delete the PVC and recreate it with a smaller request, or fix the StorageClass to use a different provisioner. If the tank leaks after a power outage, you ask: was the water (data) stored in the tank’s own volume (ephemeral) or in a separate bucket (external storage like NFS)? Kubernetes persistence means the data survives even if the container restarts – like a tank that holds water even when you move the stand.
In Kubernetes, containers are temporary. When a container restarts, any data written inside its own filesystem disappears – unless that data is stored in a volume. A volume is like a USB drive you plug into a container. The data on the USB drive stays even if the computer reboots.
Kubernetes manages storage through three main objects: PersistentVolume (PV), PersistentVolumeClaim (PVC), and StorageClass. A PersistentVolume is a piece of storage in the cluster – think of it as a 100 GB hard drive that an administrator has set up. A PersistentVolumeClaim is a request for storage by a user – like saying “I need 50 GB of that hard drive”. The system matches the claim to an available volume that meets the request’s size and access modes (e.g., ReadWriteOnce – only one pod can write at a time; ReadOnlyMany – many pods can read; ReadWriteMany – many pods can write). TROUBLESHOOTING comes into play when the PVC stays in a Pending state. This happens when no PV matches the PVC’s requirements. Common reasons: the requested size is larger than any PV; the access mode does not match; the StorageClass specified in the PVC does not exist or is misconfigured.
StorageClass is the third piece. It tells Kubernetes how to provision a new PV dynamically. For example, if you define a StorageClass named “fast” that references a specific cloud provider’s SSD provisioner, then when a PVC asks for “fast” storage, Kubernetes automatically creates a PV backed by an SSD. Without a StorageClass, you must create PVs manually (static provisioning). Dynamic provisioning is more common in real clusters.
When troubleshooting, the first command is usually kubectl get pvc to see if the claim is Bound or Pending. Then kubectl describe pvc <name> shows events and details. Common error: “Failed to provision volume with StorageClass” – this means the provisioner (e.g., a cloud driver) is not installed correctly or the cloud credentials are wrong. Another error: “waiting for a volume to be created, either by external provisioner or by manual creation” – this means dynamic provisioning is failing or there is no matching PV.
A pod that uses a PVC must have the volume mounted correctly. If the pod starts but cannot write to the volume, check the access modes. If the pod crashes with “permission denied”, the filesystem permissions on the volume might be wrong. Using a StatefulSet (a controller that gives each pod a unique stable identity and persistent storage) often uses a VolumeClaimTemplate – if one pod fails, its replacement gets the same PVC, preserving data.
For raw block volumes (a volume presented as a block device without a filesystem), the pod must be configured to use the volume as a block device, not as a mount path. Troubleshooting those requires checking device paths with lsblk inside the node.
Another critical point: reclaim policy. PVs have three reclaim policies: Retain (volume kept after PVC deletion, but needs manual cleanup), Recycle (deprecated, wipes data), and Delete (volume deleted along with PVC). If a volume is lost after deleting a PVC, check the reclaim policy – a Delete policy will destroy the underlying storage.
In summary, the mental model is: PV = storage resource; PVC = request ticket; StorageClass = how to make new storage. Troubleshooting is matching these three pieces correctly.
Check PVC status
Run 'kubectl get pvc' to see if the claim is Bound or Pending. If Pending, proceed to the next step. If Bound, the PVC is correctly matched to a PV.
Describe the PVC
Run 'kubectl describe pvc <name>'. Look at the Events section at the bottom. Common messages: 'no persistent volumes available for this claim' (no matching PV), 'Failed to provision volume' (StorageClass broken), or 'waiting for a volume to be created' (provisioner not responding).
Check existing PVs
Run 'kubectl get pv' to list all volumes. Compare capacity, access modes, and StorageClass. If a PV exists but is not bound, check its status: Available, Bound, Released, or Failed. A Released PV still has a claimRef and will not bind to a new PVC until that reference is cleared.
Inspect the StorageClass
Run 'kubectl get sc' and 'kubectl describe sc <name>'. Verify that the provisioner (e.g., 'kubernetes.io/aws-ebs') is correct. If the provisioner is a third-party driver (like csi-hostpath), ensure the driver pods are running in the cluster.
Verify pod volume mounts
If the PVC is Bound but the pod fails to start, run 'kubectl describe pod <name>'. Look for events like 'FailedMount' or 'Unable to attach or mount volumes'. This could indicate an issue with the volume driver, permissions, or node-level access. Check the node’s kubelet logs if needed.
Fix the identified issue
Based on the diagnosis, take corrective action: resize the PVC (if supported), change the StorageClass name, delete and recreate the PVC with corrected parameters, clear the PV's claimRef, or update the pod's securityContext to fix filesystem permissions.
Imagine you work as a junior platform engineer at a company running a customer-facing e-commerce site on Kubernetes. The site uses a PostgreSQL database running in a pod. The database stores its data on a PersistentVolume backed by a cloud SSD. One Monday morning, you get an alert: the database pod is CrashLoopBackOff. You check the pod logs – they say “cannot write to directory /var/lib/postgresql/data”. You start troubleshooting.
First, you run kubectl get pvc and see the PVC is Bound. Good. Then kubectl describe pvc postgres-pvc – the volume name, access modes, and status all look fine. Next, you check the underlying PV with kubectl describe pv <pv-name>. You notice the PV’s capacity is 100 Gi, but the pod only requested 50 Gi – that’s fine. Then you see the access mode is ReadWriteOnce, which matches the pod’s single replica. Still no obvious issue.
You SSH into the worker node where the pod is running. You run ls -la /var/lib/kubelet/pods/<pod-uid>/volumes/ to find the volume mount path. Using df -h, you see the volume is mounted. But sudo -u postgres touch /path/to/test.txt fails with “Permission denied”. You check the ownership: ls -n /path/ shows uid 999 owns the directory, but the pod runs as uid 70 (the Postgres image default). That’s the bug! The PV retained permissions from a previous backup restore. You fix it by running a one-off pod that chowns the directory to the correct uid.
Another real scenario: a developer tells you their new application cannot start because the PVC is never bound. You check the PVC and see the status is Pending. Using kubectl describe pvc, the events say “no persistent volumes available for this claim”. You look at existing PVs – all 10 Gi, but the claim asks for 200 Gi. You either create a new PV of 200 Gi or resize the existing PV (if your storage backend supports expansion). You also check if the PVC specifies a StorageClass that does not exist – the provisioner name might be misspelled. You correct the StorageClass name in the PVC definition and reapply.
A third scenario: a batch job finishes but the output data is gone. You check the pod’s volumeMounts – it used an emptyDir volume, which is ephemeral. The solution is to change the volume to a PVC that persists after the pod completes. You advise the team to use a PVC with a mount path that writes to a shared filesystem like NFS.
These real events happen every day in production clusters. The tools are: kubectl describe, kubectl get events, kubectl logs, and direct node inspection via SSH or kubectl debug. The mindset is: storage is a separate resource; failures often come from mismatched sizes, access modes, permissions, or missing provisioners.
The CKA exam devotes about 30% of its weight to Troubleshooting, and storage is a significant slice. You can expect 3-5 questions specifically about PV/PVC bindings, StorageClass misconfigurations, and pod-to-volume mapping. The exam is performance-based: you SSH into a cluster and fix issues.
Key question types:
A PVC is Pending. You must identify why and fix it. The trap: the PVC asks for a StorageClass name that does not exist. Another trap: the PV exists but has a different access mode (e.g., PV is ReadWriteOnce, PVC asks for ReadOnlyMany).
A pod fails to start with an error like “Failed to mount volume” or “MountVolume.SetUp failed”. The cause is often a missing secret for the volume driver (e.g., an iSCSI or NFS share requires authentication).
A StatefulSet pod cannot start because the PVC cannot be created. The StorageClass provisioner may be failing due to missing cloud credentials. Look for Events that say “Failed to provision volume”.
A volume is mounted but the pod has “Permission denied”. The trap: the underlying volume filesystem is owned by root, but the container runs as a non-root user. Fix by adding a securityContext with fsGroup or adjusting permissions.
You are asked to create a PVC that binds to a specific existing PV. Trap: the PV might have a claimRef set to a different PVC, preventing binding. You must clear the claimRef or create a PVC that matches exactly.
Traps the exam loves:
Misunderstanding reclaim policy: if you delete a PVC with a PV that has ReclaimPolicy: Delete, the PV and the underlying storage are gone forever. Do not rely on that PV coming back.
Volume expansion: not all storage backends support resizing. The exam may ask you to resize a PVC, but if the StorageClass has allowVolumeExpansion: false, the command will fail.
Multi-attach errors: if a PV is only ReadWriteOnce but two pods try to use it simultaneously, one pod will fail with a “multi-attach” error. The fix is either change the access mode or use a different PV.
Key definitions to memorise:
PersistentVolume (PV): a piece of storage in the cluster, provisioned by an admin or dynamically.
PersistentVolumeClaim (PVC): a request for storage by a user.
StorageClass: defines the type of storage (e.g., SSD, HDD) and the provisioner that creates it.
accessModes: ReadWriteOnce (RWO), ReadOnlyMany (ROX), ReadWriteMany (RWX).
reclaimPolicy: Retain, Recycle (deprecated), Delete.
Exam wisdom: always read the events at the bottom of kubectl describe. 90% of the answer is in those events. And if a pod is stuck in ContainerCreating, the issue is almost always volume mounting. Run kubectl describe pod first – it saves time.
A PersistentVolume is a storage resource in the cluster; a PersistentVolumeClaim is a request that binds to a matching PV.
A PVC stays Pending if no PV matches its size, access mode, or StorageClass – always check commands 'kubectl get pvc' and 'kubectl describe pvc' first.
StorageClass defines how to dynamically provision PVs; if the provisioner is missing or misconfigured, PVCs will never bind.
Reclaim policy on a PV controls what happens when the PVC is deleted: Retain keeps the volume, Delete removes it.
Access modes (ReadWriteOnce, ReadOnlyMany, ReadWriteMany) must match between PVC and PV – mismatches cause binding failures.
When a pod is stuck in ContainerCreating, the cause is almost always a volume mount failure – inspect pod events with 'kubectl describe pod'.
Volume permissions are not managed by Kubernetes – if a container runs as non-root, you must set the correct UID/GID on the mounted filesystem using fsGroup or a securityContext.
These come up on the exam all the time. Here's how to tell them apart.
Static Provisioning
Admin manually creates PVs before any PVC exists.
The PVC binds only if exact match of size/access/StorageClass exists.
You must manage storage capacity and tear down PVs manually.
Dynamic Provisioning
StorageClass automatically creates a PV when a PVC is submitted.
The PVC binds as soon as the provisioner finishes creating the volume.
StorageClass handles capacity, but you must ensure the provisioner is running.
ReadWriteOnce (RWO)
Only one pod can mount the volume as read-write at a time.
Commonly used for single-replica databases.
If two pods try to use it simultaneously, one gets a multi-attach error.
ReadWriteMany (RWX)
Many pods can mount the volume as read-write simultaneously.
Typically used for shared filesystems like NFS or cloud file stores.
Requires storage backend that supports concurrent writes (e.g., NFS, CephFS).
Reclaim Policy: Retain
When PVC is deleted, the PV remains in Released status.
The underlying storage (e.g., cloud disk) is NOT deleted.
You must manually clear claimRef and recycle the volume.
Reclaim Policy: Delete
When PVC is deleted, the PV and underlying storage are deleted immediately.
Data is permanently lost unless backed up externally.
Useful for temporary or non-critical workloads.
PersistentVolumeClaim (PVC)
A cluster-level request for persistent storage.
Survives pod deletion and can be reused by another pod.
Requires explicit binding to a PV before use.
Volume (container-level)
A pod-level reference to a storage source (e.g., hostPath, emptyDir).
Tied to the pod lifecycle – lost when pod is removed from node.
No binding step – the volume is used directly.
Mistake
If I delete a PVC, the PV is automatically deleted too.
Correct
The PV is only deleted if its reclaimPolicy is set to Delete. If the policy is Retain, the PV remains but becomes Released – you must manually clean it up.
People conflate PVC deletion with resource cleanup, not realising the PV lifecycle is separate from the PVC lifecycle.
Mistake
A PVC can bind to any PV regardless of access modes.
Correct
The PVC must specify an access mode that is either the same or a subset of the PV’s mode. For example, a PVC requesting ReadWriteOnce cannot bind to a PV that only supports ReadOnlyMany.
Beginners think capacity is the only matching criterion, but access modes are a strict filter.
Mistake
You can change a PV’s reclaimPolicy after it is created.
Correct
You can edit a PV’s reclaimPolicy – it is not immutable. But changing it does not affect existing PVC deletions already executed.
Many resources in Kubernetes are immutable (e.g., pod spec), but PV specs can be updated; this confuses new learners.
Mistake
StorageClass is only needed for dynamic provisioning; static PVs do not use it.
Correct
A static PV can have a StorageClass name set, and a PVC with the same name will prefer that PV. If the PVC specifies a StorageClass but the PV has a different class, they will not bind.
People assume StorageClass is optional for static PVs, but it influences matching behaviour.
Mistake
A PVC with status Pending always means the cluster is out of storage.
Correct
Pending can also mean the StorageClass provisioner is broken, the access mode mismatch, or the PV has a claimRef pointing elsewhere.
Beginners jump to the most dramatic cause (out of disk) without reading events.
Mistake
EmptyDir volumes are persistent and survive pod restarts.
Correct
EmptyDir volumes are ephemeral – they are created when a pod starts and deleted when the pod is removed from a node. They only survive container crashes within the same pod, not pod deletion.
The name ‘emptyDir’ is misleading; it sounds like a persistent empty directory, but it is actually temporary storage tied to the pod’s lifecycle.
Reveal each answer, then mark whether you got it right. Score 60%+ to unlock the next chapter.
The PVC cannot find a matching PersistentVolume. Check with 'kubectl describe pvc' for events. Common causes: no PV with the requested size or access mode, missing StorageClass, or the StorageClass provisioner is not working.
Set the PV's reclaimPolicy to Retain before deleting the PVC. After deletion, the PV will be in 'Released' status. You must then manually clear its claimRef by editing the PV to make it 'Available' again for future claims.
Yes, if the underlying StorageClass allows volume expansion (allowVolumeExpansion: true). Edit the PVC to increase the storage request, but the actual resizing depends on the storage backend supporting it.
A StatefulSet uses a VolumeClaimTemplate, which creates a unique PVC for each replica. Regular PVCs are created independently. StatefulSet PVCs are usually retained even when the pod is deleted, preserving data for the replacement pod.
The volume’s filesystem permissions likely do not match the container user. Add a securityContext to the pod with fsGroup or runAsUser that matches the expected owner. Or use an initContainer to chown the mount path.
If the pod uses a PVC, the data persists on the volume even if the container restarts. If the pod uses emptyDir, the data is lost when the pod is removed from the node (though it survives a container crash within the same pod).
You've finished Troubleshooting Storage Persistence. Continue through the CKA study guide to build a complete picture of the exam.
Done with this chapter?