Kubernetes PV reclaim policies: Retain, Delete, Recycle in practice
Deleting a PVC does not always delete the underlying storage. If the PV enters Released state and the cloud bill keeps growing, or the application fails after redeployment because the PV is still bound to a deleted claim, the reclaim policy is misaligned with operator intent. This guide explains what happens when a bound PVC is deleted under each reclaim policy, how to recover a Retained volume, why Delete can orphan cloud resources, and why Recycle should not be used in modern clusters. The focus is on practical checks, CSI-specific gotchas, and preventing data loss.
What this means
When a PVC is deleted, the Kubernetes PV controller evaluates the bound PV’s persistentVolumeReclaimPolicy. The policy determines whether the backing storage asset and the PV object survive.
Retain: The PV transitions to Released. Both the PV object and the backing storage asset persist. Data remains intact. The PV is not available for a new claim until an admin clears claimRef from the PV spec and recreates a PVC that references it by name. The admin must also manually delete the backing storage asset from the cloud provider when ready.
Delete: The PV controller triggers deletion of the backing storage asset via the storage provisioner. For CSI volumes, this means the ControllerDeleteVolume RPC. Both the PV object and the cloud resource are removed automatically.
Recycle: This policy is deprecated. It runs rm -rf /thevolume/* on the volume to scrub data, then returns the PV to the Available pool. As of Kubernetes 1.36, only nfs and hostPath volume plugins support Recycle. CSI volumes have no Recycle path at all.
Dynamically provisioned volumes inherit their reclaimPolicy from the StorageClass. Most StorageClasses default to Delete. Static PVs have their own spec.persistentVolumeReclaimPolicy field set at creation time, and this field overrides any StorageClass-level policy.
There are important version differences. Before Kubernetes 1.31, if the PV was deleted before its bound PVC, the reclaim policy was ignored. The PV object vanished but the backing storage asset remained in the cloud, causing orphaned resources. Kubernetes 1.31 introduced deletion-protection finalizers as a beta feature, and 1.33 graduated them to GA. On 1.33+, PVs with Delete policy are reliably cleaned up regardless of whether the PV or PVC is deleted first.
Common causes
| Cause | What it looks like | First thing to check |
|---|---|---|
| StorageClass defaults to Delete | PVC deletion wipes data that operators expected to keep | kubectl get storageclass <name> -o jsonpath='{.reclaimPolicy}' |
| Static CSI PV missing provisioner annotation | PV deleted but EBS or EFS volume remains in the cloud console | kubectl get pv <name> -o jsonpath="{.metadata.annotations['pv.kubernetes.io/provisioned-by']}" |
| Volume still attached to a node | PV stuck in Terminating; CSI driver reports attachment error | kubectl get volumeattachment |
| Retained PV claimRef not cleared | Application redeploys but cannot bind because claimRef still points to the deleted PVC | kubectl get pv <name> -o jsonpath='{.spec.claimRef}' |
| Recycle policy on unsupported volume type | PV stays in Released or fails to transition because no recycler exists | PV persistentVolumeReclaimPolicy and volume plugin type |
| Namespace deletion with Retain policy | Namespace and PVC are gone, PV is Released, but cloud storage accrues cost | Cloud console for orphaned resources |
Quick checks
# Check PV reclaim policy and current phase
kubectl get pv <name> -o jsonpath='{.spec.persistentVolumeReclaimPolicy}{"\t"}{.status.phase}{"\n"}'
# Verify StorageClass reclaim policy for dynamically provisioned volumes
kubectl get storageclass <name> -o jsonpath='{.reclaimPolicy}'
# Verify static CSI PV has the provisioner annotation
kubectl get pv <name> -o jsonpath="{.metadata.annotations['pv.kubernetes.io/provisioned-by']}"
# Check deletion-protection finalizers on PV (Kubernetes 1.31+)
kubectl get pv <name> -o jsonpath='{.metadata.finalizers}'
# Check if volume is still attached to a node
kubectl get volumeattachment | grep <pv-name>
# Find all Released PVs that may need manual cleanup
kubectl get pv --field-selector=status.phase=Released
# Look for provisioning or attach errors in events
kubectl get events --field-selector reason=ProvisioningFailed
kubectl get events --field-selector reason=FailedAttachVolume
How to diagnose it
- Identify the PV bound to the deleted PVC. Check
kubectl get pvand match the PV name that was listed in the PVC’sspec.volumeName. - Check the PV phase. Released means Retain. Terminating means Delete is in progress. Failed means reclamation encountered an error.
- Read the PV’s
spec.persistentVolumeReclaimPolicy. For dynamically provisioned volumes, confirm this matches the originating StorageClass or check if it was patched separately. - If the policy is Delete and the PV is stuck in Terminating, check VolumeAttachment objects. A volume still attached to a node blocks CSI
ControllerDeleteVolume. Look for events indicating the volume is still attached. - If the policy is Delete and the backing cloud resource still exists after the PV object is gone on a pre-1.31 cluster, you are seeing the out-of-order deletion leak. The PV controller deleted the object without waiting for the provisioner. On newer clusters with deletion-protection finalizers, verify that the external-provisioner finalizer is present on the PV.
- For static CSI PVs with Delete policy, verify the
pv.kubernetes.io/provisioned-byannotation. Without it, the external-provisioner skips deletion entirely and the backing resource orphans silently. - If the policy is Retain and you need to reuse the PV, plan for manual recovery. Patch
claimRefto null and create a new PVC withvolumeNameset to the PV name.
flowchart TD
A[PVC deleted] --> B{PV reclaim policy}
B -->|Retain| C[PV phase: Released]
B -->|Delete| D[Trigger ControllerDeleteVolume]
B -->|Recycle| E[Deprecated unsupported for CSI]
C --> F[Admin clears claimRef]
F --> G[PV: Available]
G --> H[New PVC binds via volumeName]
D --> I{Volume attached?}
I -->|Yes| J[PV: Terminating stuck]
I -->|No| K[Backing storage deleted]
K --> L[PV object deleted]Metrics and signals to monitor
| Signal | Why it matters | Warning sign |
|---|---|---|
| PV phase count in Released | Released PVs indicate manual cleanup debt or unexpected Retain behavior | Number of Released PVs increasing over time |
| PV phase count in Terminating | Terminating PVs that do not complete suggest attachment or provisioner blockers | PVs stuck in Terminating longer than 5 minutes |
| StorageClass reclaimPolicy | Determines default behavior for all new dynamic volumes | Delete set on a class used for irreplaceable stateful data |
| CSI provisioner annotation on static PVs | Missing annotation causes silent orphaning under Delete policy | Static PV with Delete policy but no pv.kubernetes.io/provisioned-by annotation |
| VolumeAttachment objects | Stuck attachments block Delete reclamation | VolumeAttachment exists for a PV that should already be deleted |
| Cloud storage costs | Orphaned resources from failed Delete policy show up as unexpected spend | Unexplained growth in unattached disk costs |
| PVC deletion event rate | Correlate PVC deletions with PV state transitions | PVC deleted but PV does not reach expected end state within the sync window |
Fixes
If the cause is an unexpected Delete policy
Patch the PV to Retain before deleting the PVC. This is a safe, read-only change to the PV spec that prevents automatic deletion of the backing asset.
kubectl patch pv <name> -p '{"spec":{"persistentVolumeReclaimPolicy":"Retain"}}'
Then delete the PVC. The PV will become Released and the data will persist.
If the cause is a stuck Terminating PV
A volume still attached to a node prevents the CSI driver from issuing DeleteVolume. Wait for the pod to fully terminate and for the VolumeAttachment to clear.
Warning: Force-detaching a volume from a running node through the cloud provider console or API can cause data corruption. Only force-detach if the node is fully terminated or unrecoverable. Once detached, the PV should complete deletion.
If the cause is a Retained PV that must be reused
Patch the PV to clear the claim reference, which allows rebinding.
kubectl patch pv <name> -p '{"spec":{"claimRef": null}}'
Then create a new PVC with volumeName: <pv-name> in its spec. The PVC will bind to the existing PV and the data will be accessible again.
If the cause is a missing CSI annotation on a static PV
If a static PV with Delete policy has already orphaned its backing storage because the annotation was missing, you must manually delete the cloud resource. For new static PVs, always add the pv.kubernetes.io/provisioned-by annotation naming the correct CSI driver at creation time.
If the cause is a StorageClass default you want to change
Edit the StorageClass reclaimPolicy field. This affects only future dynamically provisioned volumes. Existing PVs must be patched individually if you need to change their policy.
Prevention
- Set StorageClass defaults intentionally. Do not assume the default is Retain. Most distributions and cloud StorageClasses default to Delete.
- Treat Retain as the safer default for any volume containing data you cannot regenerate. Change the StorageClass or patch individual PVs before PVC deletion.
- For static CSI PVs using Delete, verify the
pv.kubernetes.io/provisioned-byannotation at creation time. Without it, you have a silent orphaning risk. - Monitor Released PVs. A growing count means manual cleanup is not keeping up, or teams are deleting PVCs without understanding the policy.
- On older clusters without deletion-protection finalizers, be aware of the out-of-order deletion leak. Avoid deleting PVs directly while their PVCs still exist.
- Do not use Recycle. It is deprecated and unsupported for CSI volumes. Use dynamic provisioning with Retain or Delete instead.
- Document your recovery runbook for Retained volumes. Operators need to know the exact steps to clear
claimRefand recreate PVCs.
How Netdata helps
Netdata monitors node disk utilization and mount state. Use these metrics to correlate storage pressure with volume attachment events or to detect unexpected disk consumption from Retained volumes that are still mounted.
Related guides
- For control plane issues that can delay VolumeAttachment updates and block PV deletion, see Kubernetes API server slow or unresponsive: causes and fixes.
- If CSI driver failures are preventing Delete reclamation, see Kubernetes CSI driver failures: detection, recovery, and version skew.
- For scheduling and pod lifecycle issues that can leave volumes attached to nodes, see Kubernetes Deployment rollout stuck: stalled rollouts and ready replicas.






