PersistentVolumeClaims (PVCs) are a normal part of stateful Kubernetes workloads. Applications use PVCs to request persistent storage, while PersistentVolumes (PVs) provide the actual storage resource.

The problem is that PVCs can remain after the workload that created them has been removed.

Over time, a cluster can accumulate:

Kubernetes does not automatically know whether a PVC is still important to an administrator simply because it is no longer attached to a running application.

That makes identifying unused storage an operational task.

Understanding the PVC Relationship

A simplified Kubernetes storage model looks like this:

Pod
 |
 v
PersistentVolumeClaim
 |
 v
PersistentVolume
 |
 v
Storage Backend

A pod can mount a PVC:

apiVersion: v1
kind: Pod
metadata:
  name: storage-demo
spec:
  containers:
    - name: app
      image: nginx
      volumeMounts:
        - name: app-data
          mountPath: /data

  volumes:
    - name: app-data
      persistentVolumeClaim:
        claimName: app-data

The pod references the PVC through:

claimName: app-data

The PVC then represents the application's request for persistent storage.

What Makes a PVC "Unused"?

An unused PVC does not necessarily mean it is safe to delete.

A claim might be:

Therefore, "not currently mounted" and "safe to delete" are not the same thing.

A useful investigation should combine Kubernetes resource information with application ownership and operational context.

Step 1: List PVCs

Start by viewing claims across namespaces:

kubectl get pvc -A

For a specific namespace:

kubectl get pvc -n production

A typical result may look like:

NAME             STATUS   VOLUME       CAPACITY   ACCESS MODES   AGE
app-data         Bound    pvc-abc123   20Gi       RWO            45d
database-data    Bound    pvc-def456   100Gi      RWO            90d
old-test-data    Bound    pvc-ghi789   50Gi       RWO            180d

The AGE field can help identify old resources, but age alone is not evidence that a claim is unused.

Step 2: Check the PVC Details

Inspect a suspicious claim:

kubectl describe pvc old-test-data -n production

This provides information such as:

You can also retrieve the object as YAML:

kubectl get pvc old-test-data \
  -n production \
  -o yaml

The YAML is useful when investigating metadata such as labels, annotations, and ownership information.

Step 3: Find Pods Using a PVC

A PVC is commonly consumed by a pod through its volumes definition.

You can inspect pods in a namespace:

kubectl get pods -n production -o yaml

Then search for:

persistentVolumeClaim:
  claimName: old-test-data

For a single namespace, this provides direct evidence that a pod currently references the claim.

For larger environments, automate the search rather than manually checking hundreds of pods.

Finding PVC References with JSONPath

You can inspect pod volumes using JSONPath:

kubectl get pods -n production \
  -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{range .spec.volumes[*]}{.persistentVolumeClaim.claimName}{" "}{end}{"\n"}{end}'

This produces output similar to:

api-7c8f9d    app-data
worker-55fd21
database-0    database-data

You can compare these references with the PVC list.

A claim that does not appear in current pod volume references deserves further investigation.

Step 4: Check StatefulSets

Stateful applications require additional care.

For example:

kubectl get statefulsets -A

A StatefulSet can use volumeClaimTemplates to create PVCs for individual replicas.

Example:

volumeClaimTemplates:
  - metadata:
      name: database-data
    spec:
      accessModes:
        - ReadWriteOnce
      resources:
        requests:
          storage: 100Gi

A generated PVC may have a name such as:

database-data-database-0

Deleting such a claim without understanding the StatefulSet's storage design can result in data loss.

Step 5: Check Deployments and Jobs

Not all storage users are long-running applications.

Review:

kubectl get deployments -A
kubectl get jobs -A
kubectl get cronjobs -A

Temporary workloads can create storage that remains after the workload finishes.

This is particularly common in:

Step 6: Inspect the PersistentVolume

A PVC normally binds to a PersistentVolume.

Find the associated PV:

kubectl get pvc old-test-data \
  -n production \
  -o jsonpath='{.spec.volumeName}'

Then inspect it:

kubectl describe pv <volume-name>

Check:

The reclaim policy is particularly important.

Understanding Reclaim Policies

A PersistentVolume can have a reclaim policy such as:

Retain
Delete

The policy determines what happens to the underlying storage after the claim is released.

A Retain policy is intentionally conservative because the storage may need manual cleanup or recovery.

A Delete policy can remove the associated storage resource when the claim is deleted, depending on the storage provisioner.

Therefore, never treat PVC deletion as a simple metadata cleanup operation.

Step 7: Check Storage Classes

Find the storage class:

kubectl get storageclass

Then inspect it:

kubectl describe storageclass <storage-class>

Storage classes can define how persistent storage is provisioned.

Understanding the storage class helps determine what deleting a PVC could mean for the underlying infrastructure.

A Safer Investigation Workflow

A useful process is:

List PVCs
   |
   v
Identify old or suspicious claims
   |
   v
Check current pod references
   |
   v
Check StatefulSets and workloads
   |
   v
Inspect the PV
   |
   v
Check reclaim policy
   |
   v
Check application ownership
   |
   v
Confirm backups / retention requirements
   |
   v
Delete only after validation

This is safer than deleting claims based solely on their age.

Automating PVC Investigation

For larger clusters, manually inspecting every claim is inefficient.

A script can first list claims:

kubectl get pvc -A \
  -o custom-columns=\
NAMESPACE:.metadata.namespace,\
NAME:.metadata.name,\
STATUS:.status.phase,\
VOLUME:.spec.volumeName

You can then investigate candidates based on namespace, labels, workload ownership, or age.

For example:

kubectl get pvc -A \
  --sort-by=.metadata.creationTimestamp

This sorts claims by creation time and makes older resources easier to review.

Remember that this is an investigation aid, not an automated deletion mechanism.

Labels Can Make Cleanup Easier

Good resource metadata makes storage management much easier.

For example:

metadata:
  labels:
    app: reporting
    environment: staging
    owner: data-team

Later, administrators can identify resources using:

kubectl get pvc \
  -n staging \
  -l environment=staging

Consistent labels are especially useful in environments containing many teams and applications.

Common Mistakes

Deleting Based Only on Age

An old PVC may contain important production data.

Assuming Bound Means Actively Used

Bound indicates that the claim is associated with a PV. It does not by itself prove that a running pod is currently using it.

Ignoring StatefulSets

Stateful workloads often have storage lifecycle behavior that differs from ordinary deployments.

Forgetting the Reclaim Policy

Deleting a PVC can have consequences beyond the Kubernetes object itself.

Cleaning Up Without Backups

Before removing important storage, verify that the data is either disposable or covered by an appropriate backup and recovery process.

Using Force Deletion as a Cleanup Strategy

Force deletion should not be used simply because a resource appears difficult to remove.

First determine why Kubernetes is retaining it.

Production Best Practices

Use Labels Consistently

Identify application, environment, team, and ownership.

Document Storage Ownership

Teams should know which workloads own persistent storage.

Review Old Claims Regularly

Periodic review can prevent storage resources from accumulating indefinitely.

Monitor Storage Capacity

Unused claims are only one part of storage management. Monitor actual capacity and utilization as well.

Protect Stateful Data

Treat database and stateful application PVCs differently from temporary test storage.

Establish a Retention Policy

Define how long storage should remain after an application is retired.

Advantages of Regular PVC Audits

Regular storage reviews can provide several benefits:

Benefit

Impact

Reduced storage waste

Fewer unnecessary resources

Better visibility

Easier cluster administration

Clear ownership

Faster troubleshooting

Improved capacity planning

Better understanding of storage demand

Safer cleanup

Less accidental deletion

Easier cost management

Fewer unnecessary storage resources

Risks of Aggressive Cleanup

Cleanup without sufficient validation can cause:

The cost of an incorrectly deleted production database PVC can be significantly greater than the storage cost of keeping an old claim temporarily.

A Practical PVC Review Checklist

Before deleting a PVC, verify:

[ ] PVC namespace identified
[ ] PVC ownership identified
[ ] Current pod references checked
[ ] StatefulSets checked
[ ] Jobs and CronJobs checked
[ ] Associated PV inspected
[ ] Storage class identified
[ ] Reclaim policy checked
[ ] Backup status verified
[ ] Data retention requirements checked
[ ] Application owner contacted when necessary
[ ] Deletion impact understood
[ ] Rollback or recovery plan available

Summary

Finding unused PVCs in Kubernetes is not simply a matter of locating old claims.

A PVC can remain important even when no pod is currently mounting it. Stateful workloads, backups, retention policies, and persistent storage configuration all need to be considered before cleanup.

Start with kubectl get pvc, inspect suspicious claims, identify current workload references, check StatefulSets, examine the associated PersistentVolume, and understand the reclaim policy.

For production clusters, the safest approach is to treat PVC cleanup as a controlled storage-management process rather than an automated deletion task.

The goal is not to delete as many unused PVCs as possible. The goal is to confidently identify storage that is genuinely no longer required without putting persistent application data at risk.