Skip to main content
mastering ckad certified kubernetes application developer

Reclaim Policy and emptyDir Solutions

6 min read Chapter 45 of 87
Summary

Step-by-step solution for Exercise 3: observing the difference...

Step-by-step solution for Exercise 3: observing the difference between Delete and Retain reclaim policies by creating PVCs with dynamic provisioning, creating a static PV with Retain policy, deleting PVCs, and inspecting PV status changes.

Reclaim Policy and emptyDir Solutions

Solution: Exercise 3 — Reclaim Policy Observation

This exercise has two parts that demonstrate the behavioral difference between the Delete and Retain reclaim policies. You will create PVCs, delete them, and observe what happens to the underlying PVs.

Part A: Delete Reclaim Policy

The standard StorageClass in Kind has a Delete reclaim policy. When a PVC using this class is deleted, the dynamically provisioned PV is also deleted along with its data.

Step 1: Create the PVC

kubectl apply -f - <<EOF
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: delete-test
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  storageClassName: standard
EOF

The PVC will show Pending because Kind’s standard StorageClass uses WaitForFirstConsumer. Create a temporary Pod to trigger provisioning:

kubectl run pv-trigger --image=busybox:1.36 --restart=Never \
  --overrides='{
    "spec": {
      "containers": [{
        "name": "busybox",
        "image": "busybox:1.36",
        "command": ["sleep", "60"],
        "volumeMounts": [{"name": "vol", "mountPath": "/mnt"}]
      }],
      "volumes": [{"name": "vol", "persistentVolumeClaim": {"claimName": "delete-test"}}]
    }
  }'

Wait for the Pod to start:

kubectl get pod pv-trigger -w

Step 2: Record the PV Name

Once the PVC is bound, record the dynamically created PV name:

PV_NAME=$(kubectl get pvc delete-test -o jsonpath='{.spec.volumeName}')
echo "PV name: $PV_NAME"

Output:

PV name: pvc-a1b2c3d4-e5f6-7890-abcd-ef1234567890

Verify the PV exists:

kubectl get pv "$PV_NAME"

Expected output:

NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                STORAGECLASS   AGE
pvc-a1b2c3d4-e5f6-7890-abcd-ef1234567890   1Gi        RWO            Delete           Bound    default/delete-test  standard       20s

Note the RECLAIM POLICY column shows Delete.

Step 3: Delete the Pod and PVC

First delete the Pod so the PVC is no longer in use:

kubectl delete pod pv-trigger

Then delete the PVC:

kubectl delete pvc delete-test

Step 4: Check the PV

kubectl get pv "$PV_NAME" 2>/dev/null || echo "PV not found — it was deleted"

Output:

PV not found — it was deleted

What happened: The Delete reclaim policy instructs Kubernetes to remove the PV object and its backing storage when the PVC is deleted. The local-path-provisioner cleaned up the directory on the node. This is the default behavior for dynamically provisioned volumes in most StorageClasses, including Kind’s standard class.

This is the expected production behavior for disposable storage — build caches, temporary computation results, test data. You do not want orphaned disks accumulating costs with no PVC referencing them.


Part B: Retain Reclaim Policy

With the Retain policy, deleting the PVC leaves the PV and its data intact. The PV transitions to Released but cannot be rebound to a new PVC without administrator intervention.

Step 1: Create the PV

apiVersion: v1
kind: PersistentVolume
metadata:
  name: retain-pv
spec:
  capacity:
    storage: 1Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: manual
  hostPath:
    path: /mnt/retain-data
    type: DirectoryOrCreate

Save as retain-pv.yaml and apply:

kubectl apply -f retain-pv.yaml

Verify the PV is available:

kubectl get pv retain-pv

Expected output:

NAME        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   AGE
retain-pv   1Gi        RWO            Retain           Available           manual          5s

The PV shows Available — no PVC has claimed it yet.

Step 2: Create the PVC

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: retain-test
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  storageClassName: manual

Save as retain-test-pvc.yaml and apply:

kubectl apply -f retain-test-pvc.yaml

Verify binding:

kubectl get pvc retain-test

Expected output:

NAME          STATUS   VOLUME      CAPACITY   ACCESS MODES   STORAGECLASS   AGE
retain-test   Bound    retain-pv   1Gi        RWO            manual         5s

The PVC has bound to retain-pv because the capacity (1Gi), access mode (RWO), and StorageClass (manual) all match. Unlike the dynamic provisioning in Part A, this is static provisioning — the PV was created ahead of time and Kubernetes found it when the PVC was submitted.

Also verify from the PV side:

kubectl get pv retain-pv

Expected output:

NAME        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                 STORAGECLASS   AGE
retain-pv   1Gi        RWO            Retain           Bound    default/retain-test   manual          30s

The CLAIM column now shows default/retain-test, confirming the binding.

Step 3: Delete the PVC

kubectl delete pvc retain-test

Step 4: Check the PV Status

kubectl get pv retain-pv

Expected output:

NAME        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS     CLAIM                 STORAGECLASS   AGE
retain-pv   1Gi        RWO            Retain           Released   default/retain-test   manual          1m

The PV is now Released, not Available. The CLAIM column still shows default/retain-test even though that PVC no longer exists.

Why the PV Is Not Available

A Released PV retains a reference to its former PVC in the spec.claimRef field. Kubernetes will not bind a new PVC to this PV because the old claim reference still exists. This is a safety mechanism — it prevents a new PVC from accidentally accessing another application’s data.

Inspect the claim reference:

kubectl get pv retain-pv -o jsonpath='{.spec.claimRef}' | python3 -m json.tool

Output:

{
    "apiVersion": "v1",
    "kind": "PersistentVolumeClaim",
    "name": "retain-test",
    "namespace": "default",
    "uid": "abc12345-def6-7890-ghij-klmn12345678"
}

How an Administrator Reuses a Retained PV

To make the PV available for a new PVC, an administrator must remove the claimRef:

kubectl patch pv retain-pv --type=json -p='[{"op": "remove", "path": "/spec/claimRef"}]'

Verify the PV is back to Available:

kubectl get pv retain-pv

Expected output:

NAME        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   AGE
retain-pv   1Gi        RWO            Retain           Available           manual          2m

Now the PV can be bound to a new PVC. The data from the previous usage is still on the hostPath directory (/mnt/retain-data). The administrator can choose to clean it up or leave it for the next consumer.

Summary: Delete vs Retain

AspectDeleteRetain
PV after PVC deletionDeleted automaticallyMoves to Released
Backing storageRemoved by provisionerLeft intact
New PVC can bind?N/A (PV gone)Not until claimRef is removed
Use caseDisposable, ephemeral dataDatabases, critical data
Default on Kind?Yes (standard class)No (must set explicitly)

Common Mistakes

Forgetting to delete the Pod before the PVC: If a Pod is still mounting a PVC, the PVC enters Terminating state but is not actually deleted until the Pod is removed. Delete the Pod first, then the PVC.

Expecting a Released PV to auto-rebind: With Retain policy, the PV stays Released indefinitely. You must manually patch out the claimRef or delete and recreate the PV.

Wrong StorageClass matching: In Part B, the PVC’s storageClassName: manual must match the PV’s storageClassName: manual. A mismatch causes the PVC to stay Pending because no PV in the manual class satisfies the request, and no provisioner exists for the manual class to create one dynamically.

Confusing Recycle with Retain: The Recycle policy (which wipes data and makes the PV available again) is deprecated. If you see it in older documentation, ignore it. The CKAD tests Retain and Delete only.

Cleanup

kubectl delete pv retain-pv --ignore-not-found