Kubernetes 1.37: Find Unused PVCs Without Custom Scripts
Use the Kubernetes 1.37 Unused PVC condition and lastTransitionTime to find orphaned storage safely across namespaces.
PersistentVolumeClaims are deliberately hard to delete by accident. That protects data, but it also means development clusters slowly collect claims that no running workload uses. Kubernetes 1.37 makes those claims easier to identify with the Beta PersistentVolumeClaimUnusedSinceTime feature.
The feature is enabled by default in Kubernetes 1.37. The PVC protection controller adds an Unused condition to each claim, so you no longer need to cross-reference every Pod and PVC just to answer: βIs any non-terminal Pod using this claim?β
What the Unused condition means
The controller reports:
| Condition | Meaning |
|---|---|
Unused=True | No non-terminal Pod currently references the PVC |
Unused=False | At least one non-terminal Pod references the PVC |
A Pending Pod still counts as using the claim because it expresses an intent to mount it. Pods in Succeeded or Failed do not count. When the final non-terminal Pod stops referencing the claim, lastTransitionTime records when the PVC became unused.
This signal is useful, but it is not permission to delete data. A StatefulSet scaled to zero, a disaster-recovery volume, or a claim retained for an audit may correctly be unused.
Inspect one PVC
kubectl get pvc app-data -n production -o jsonpath='{.status.conditions}' | jqAn unused claim looks similar to:
{
"type": "Unused",
"status": "True",
"reason": "NoPodsUsingPVC",
"lastTransitionTime": "2026-08-01T10:30:00Z"
}Confirm that no Pod references it:
kubectl get pods -A -o json | jq -r '
.items[]
| select(any(.spec.volumes[]?; .persistentVolumeClaim.claimName == "app-data"))
| "\(.metadata.namespace)/\(.metadata.name) \(.status.phase)"
'The second check is valuable before deletion because it exposes Pending workloads and references outside the namespace you expected.
List unused PVCs across the cluster
kubectl get pvc -A -o json | jq -r '
.items[]
| .status.conditions[]? as $condition
| select($condition.type == "Unused" and $condition.status == "True")
| [
.metadata.namespace,
.metadata.name,
.spec.resources.requests.storage,
$condition.lastTransitionTime
]
| @tsv
'To find claims unused for more than 30 days:
kubectl get pvc -A -o json | jq -r '
.items[]
| select(.status.conditions[]? | select(.type == "Unused" and .status == "True"))
| (.status.conditions[] | select(.type == "Unused")) as $unused
| select((now - ($unused.lastTransitionTime | fromdateiso8601)) > (30 * 86400))
| "\(.metadata.namespace)/\(.metadata.name) unused since \($unused.lastTransitionTime)"
'Build a safe review workflow
Do not pipe this query into kubectl delete. Use it to create a review queue:
- Label the claim with an owner, application, environment, and retention policy.
- Check whether a StatefulSet, CronJob, backup process, or paused environment expects it.
- Record the StorageClass and the PersistentVolume reclaim policy.
- Take and verify a snapshot when policy requires one.
- Require an owner approval for production claims.
- Delete in a non-production environment first and confirm the application can recover.
Inspect the backing PV before making a decision:
PV=$(kubectl get pvc app-data -n production -o jsonpath='{.spec.volumeName}')
kubectl get pv "$PV" -o custom-columns='NAME:.metadata.name,CLASS:.spec.storageClassName,RECLAIM:.spec.persistentVolumeReclaimPolicy,STATUS:.status.phase'The reclaim policy determines what happens to the backing storage after the claim is deleted. Treat that as a separate destructive decision.
Monitor rather than auto-delete
A safer automation exports the condition as inventory and opens a ticket after a threshold. Useful fields include namespace, claim, requested capacity, StorageClass, owner label, lastTransitionTime, PV name, and reclaim policy.
That approach turns the native condition into a cost-control signal without allowing one script bug to erase storage.
Compatibility notes
- The feature is Beta and enabled by default in Kubernetes 1.37.
- Older clusters may not populate the
Unusedcondition. - The condition tracks Pod references, not business value, backup requirements, or application ownership.
- Pending Pods count as users; terminal Pods do not.
Preparing for Kubernetes operations or the CKA exam? Explore this hands-on CKA preparation course on Udemy. Affiliate link: DevOpsBoys may earn a commission at no extra cost to you.
Sources
Today I Fixed
Short real fixes from production β posted daily
Stay ahead of the curve
Get the latest DevOps, Kubernetes, AWS, and AI/ML guides delivered straight to your inbox. No spam β just practical engineering content.
Related Articles
Kubernetes 1.37 PVC Last-Used Tracking: Find Idle Storage Safely
Use the Kubernetes 1.37 PVC Unused condition and lastTransitionTime to identify idle volumes without turning a useful signal into unsafe automatic deletion.
EKS Auto Mode: Migrate EBS Volume Modifications to VolumeAttributesClass
Replace deprecated EKS Auto Mode volume-modifier annotations with VolumeAttributesClass before October 31, 2026.
Kubernetes 1.37 Storage Hardening: Secure emptyDir with noexec, nosuid and mode
Harden Kubernetes writable volumes using v1.37 bindMountOptions and emptyDir mode, with manifests, tests, and Alpha feature warnings.