CKA — Persistent Volume Claims

5 minute read

CKA Exam Domain

Storage — 10%

PersistentVolumeClaims are how users request storage in Kubernetes. On the CKA exam, you need to create PVCs, understand how binding works, and mount them in Pods — all from memory.

What Is a PVC?

A PersistentVolumeClaim (PVC) is a user’s request for storage. Think of it as a “storage ticket” — you specify how much storage you need and what access mode you require, and Kubernetes finds a matching PV to bind to.

Key points:

  • PVCs are namespaced (unlike PVs which are cluster-scoped)
  • A PVC binds to exactly one PV (1:1 relationship)
  • Once bound, the PVC exclusively owns that PV — no other PVC can use it
  • Pods reference PVCs, not PVs directly

PVC Spec

Here is a complete PVC definition:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-demo
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 3Gi
  storageClassName: manual

Key fields:

Field Description
accessModes How the volume should be mounted (must match or be a subset of the PV’s access modes)
resources.requests.storage Minimum amount of storage needed
storageClassName Must match the PV’s storageClassName for binding

Exam tip: If the PVC’s storageClassName does not match the PV’s storageClassName, binding will not happen — even if capacity and access modes match. This is a common gotcha.

How Binding Works

When you create a PVC, Kubernetes looks for a PV that satisfies all of the following:

  1. storageClassName — Must match exactly
  2. accessModes — PV must support the requested access mode(s)
  3. capacity — PV capacity must be greater than or equal to the requested amount

The binding process:

PVC created → Scheduler looks for matching PV → PV found → Bound (1:1)
                                               → No match → PVC stays Pending

Important details:

  • Binding is 1:1 — one PVC binds to one PV
  • If a PVC requests 3Gi and the only available PV has 10Gi, it will bind (but the remaining 7Gi is wasted)
  • If no PV matches, the PVC stays in Pending state until a suitable PV becomes available

Claim Selectors

You can use matchLabels in a PVC to target a specific PV:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-fast
  labels:
    type: ssd
    environment: production
spec:
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteOnce
  storageClassName: manual
  hostPath:
    path: /data/fast
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-fast
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi
  storageClassName: manual
  selector:
    matchLabels:
      type: ssd
      environment: production

This PVC will only bind to a PV that has both labels type: ssd and environment: production.

Using PVCs in Pods

Once a PVC is bound, you reference it in a Pod spec:

apiVersion: v1
kind: Pod
metadata:
  name: app-with-storage
spec:
  containers:
    - name: app
      image: nginx
      volumeMounts:
        - mountPath: /data
          name: storage
  volumes:
    - name: storage
      persistentVolumeClaim:
        claimName: pvc-demo

The flow:

Pod spec → volumes[].persistentVolumeClaim.claimName → PVC → PV → Actual storage

Exam tip: The volumes[].name field must match the volumeMounts[].name field in the container. This is a common mistake under exam pressure.

Full End-to-End Example

Here is PV + PVC + Pod together:

# 1. PersistentVolume
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-demo
spec:
  capacity:
    storage: 5Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: manual
  hostPath:
    path: /data/pv-demo
---
# 2. PersistentVolumeClaim
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-demo
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 3Gi
  storageClassName: manual
---
# 3. Pod
apiVersion: v1
kind: Pod
metadata:
  name: pod-demo
spec:
  containers:
    - name: app
      image: nginx
      volumeMounts:
        - mountPath: /usr/share/nginx/html
          name: web-storage
  volumes:
    - name: web-storage
      persistentVolumeClaim:
        claimName: pvc-demo

PVC Protection

Kubernetes has Storage Object in Use Protection enabled by default. This prevents accidental deletion of PVCs that are actively used by Pods.

How it works:

  • When a PVC is in use by a Pod, Kubernetes adds a finalizer: kubernetes.io/pvc-protection
  • If you try to kubectl delete pvc, the PVC enters a Terminating state but is not actually deleted
  • Once all Pods using the PVC are deleted, the PVC is removed

You can verify the finalizer:

kubectl get pvc pvc-demo -o yaml | grep -A 2 finalizers

kubectl Commands

Essential PVC commands for the exam:

# List all PVCs in the current namespace
kubectl get pvc

# List PVCs in all namespaces
kubectl get pvc -A

# Describe a PVC for detailed information
kubectl describe pvc pvc-demo

# Delete a PVC
kubectl delete pvc pvc-demo

# Check PVC in YAML format
kubectl get pvc pvc-demo -o yaml

Expected output of kubectl get pvc:

NAME       STATUS   VOLUME    CAPACITY   ACCESS MODES   STORAGECLASS   AGE
pvc-demo   Bound    pv-demo   5Gi        RWO            manual         30s

Practice Questions

Q1. Create a PVC named claim-log that requests 50Mi of storage with ReadWriteOnce access mode and storageClassName manual.

Show Answer
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: claim-log
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 50Mi
  storageClassName: manual
kubectl apply -f claim-log.yaml
kubectl get pvc claim-log

Q2. A PVC is stuck in Pending state. What are the possible reasons?

Show Answer

Common reasons:

  1. No matching PV — No PV with matching storageClassName, accessModes, or sufficient capacity
  2. StorageClass does not exist — The referenced StorageClass has not been created
  3. volumeBindingMode: WaitForFirstConsumer — The PV will not bind until a Pod using this PVC is scheduled
  4. All matching PVs are already bound — PV binding is 1:1

Debug with:

kubectl describe pvc <pvc-name>
# Check the Events section for clues

Q3. Create a Pod named web-pod using image nginx that mounts a PVC called web-pvc at /usr/share/nginx/html.

Show Answer
apiVersion: v1
kind: Pod
metadata:
  name: web-pod
spec:
  containers:
    - name: nginx
      image: nginx
      volumeMounts:
        - mountPath: /usr/share/nginx/html
          name: web-storage
  volumes:
    - name: web-storage
      persistentVolumeClaim:
        claimName: web-pvc

Q4. What happens if you delete a PVC that is currently in use by a running Pod?

Show Answer

The PVC enters a Terminating state but is not actually deleted. Kubernetes Storage Object in Use Protection (via the kubernetes.io/pvc-protection finalizer) prevents deletion until all Pods using the PVC are removed.

Once the Pod is deleted, the PVC will be fully removed.

Q5. A PVC requests 2Gi storage but the only available PV has 10Gi. Will the PVC bind? What happens to the extra 8Gi?

Show Answer

Yes, the PVC will bind to the 10Gi PV (assuming storageClassName and accessModes match). The PV capacity must be greater than or equal to the PVC request.

The extra 8Gi is effectively wasted — no other PVC can bind to this PV because PV-to-PVC binding is 1:1. This is one reason dynamic provisioning with StorageClasses is preferred, as it creates a PV with exactly the requested size.

Updated: