Persistent Volumes & Claims
A MariaDB Deployment in namespace mariadb was deleted by mistake. A retained PV named mariadb-pv (300Mi, storageClass: standard) already exists.
Live lab access
This task description is public. Its live lab requires full access; six other CKA labs are available in Free Practice with AI Lab Coach.
Create an account to continue →
Explore the six free labs with AI Lab Coach →
Read the brief on any device. Use a desktop or laptop for the terminal lab. Nothing starts until you choose Start.
The task
A MariaDB Deployment in namespace mariadb was deleted by mistake. A retained PV named mariadb-pv (300Mi, storageClass: standard) already exists.
Task 1: Create a PVC in namespace mariadb:
- Name:
mariadb - Storage:
250Mi - AccessMode:
ReadWriteOnce - Must bind to the existing PV
mariadb-pv
Task 2: Update ~/mariadb-deployment.yaml to mount the PVC at /var/lib/mysql and apply it.
Original Prepium workshop · Tested on Kubernetes v1.35.0 on 1 October 2026 in a three-node kind cluster whose default StorageClass is standard. Use a disposable cluster. The example PersistentVolume is a hostPath volume, which is fine for learning binding rules but stores data on one node only.
Bind a PersistentVolumeClaim to one specific PersistentVolume, and reuse it after a Retain
A PersistentVolume (PV) is cluster-scoped storage; a PersistentVolumeClaim (PVC) is a namespaced request for storage. The control plane binds a claim to a volume only when the StorageClass name, the access modes and the size are all compatible. Setting spec.volumeName on the claim pins it to one named PV, but it does not relax any of those rules.
PV archive-pv (1Gi, RWO, class manual, Retain) ←── bound ─── PVC archive-claim (500Mi, RWO, class manual)
↑ mounted by Pod archive-reader at /data
1. Create a static PV
Save this as pv-static.yaml. The manual class does not need to exist as a StorageClass object; for static volumes it is just a label that claims must repeat.
apiVersion: v1
kind: PersistentVolume
metadata:
name: archive-pv
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: manual
hostPath:
path: /tmp/prepium-archive
type: DirectoryOrCreate
kubectl create namespace prepium-tutorial
kubectl apply -f pv-static.yaml
kubectl get pv archive-pv
The PV should be Available, with no claim.
2. Make the classic mistake first
Create a claim that names the volume but leaves out storageClassName:
cat <<'EOF' | kubectl apply -f -
apiVersion: v1
kind: PersistentVolumeClaim
metadata: {name: wrong-class, namespace: prepium-tutorial}
spec:
volumeName: archive-pv
accessModes: [ReadWriteOnce]
resources: {requests: {storage: 500Mi}}
EOF
kubectl -n prepium-tutorial get pvc wrong-class -o jsonpath='{.spec.storageClassName}{"\n"}'
kubectl -n prepium-tutorial describe pvc wrong-class
The claim comes back with storageClassName: standard even though you never wrote it. When a cluster has a default StorageClass, the DefaultStorageClass admission plugin fills the field in for any claim that omits it. The claim then stays Pending with VolumeMismatch: Cannot bind to requested volume "archive-pv": storageClassName does not match. Omitting the field is not the same as matching a PV that has a class. Write the PV's class explicitly (or storageClassName: "" for a PV that has none).
kubectl -n prepium-tutorial delete pvc wrong-class
3. Bind correctly and mount it
Save this as pvc-archive.yaml. It contains the claim and a Pod that appends a timestamp to a file on the volume.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: archive-claim
namespace: prepium-tutorial
spec:
storageClassName: manual
volumeName: archive-pv
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 500Mi
---
apiVersion: v1
kind: Pod
metadata:
name: archive-reader
namespace: prepium-tutorial
spec:
containers:
- name: shell
image: busybox:1.36
command: ["/bin/sh", "-c", "date >> /data/visits.txt; sleep 3600"]
volumeMounts:
- name: archive
mountPath: /data
volumes:
- name: archive
persistentVolumeClaim:
claimName: archive-claim
kubectl apply -f pvc-archive.yaml
kubectl -n prepium-tutorial get pvc archive-claim
kubectl -n prepium-tutorial wait --for=condition=Ready pod/archive-reader
kubectl -n prepium-tutorial exec archive-reader -- cat /data/visits.txt
The claim is Bound and its CAPACITY column says 1Gi, not the 500Mi you asked for. Binding is one-to-one: a claim gets the whole volume, and the request is only a minimum.
4. Delete the claim and reuse the retained volume
kubectl -n prepium-tutorial delete pod archive-reader
kubectl -n prepium-tutorial delete pvc archive-claim
kubectl get pv archive-pv
With persistentVolumeReclaimPolicy: Retain the data is kept and the PV moves to Released. Its CLAIM column still shows prepium-tutorial/archive-claim. Apply pvc-archive.yaml again and the new claim stays Pending: the PV still holds a claimRef with the old claim's UID, so it refuses a new claim even one with the same name.
kubectl get pv archive-pv -o jsonpath='{.spec.claimRef.uid}{"\n"}'
kubectl patch pv archive-pv --type=json -p '[{"op":"remove","path":"/spec/claimRef"}]'
kubectl -n prepium-tutorial get pvc archive-claim
Removing the stale claimRef returns the PV to Available, and the waiting claim binds within seconds. Clearing it is a deliberate administrator action: you are confirming that whoever owned the old data is finished with it. In our run the reader was scheduled onto the same node again and printed both timestamps; with hostPath on a different node you would see a fresh file, which is why real clusters use network or CSI volumes for data that must follow a Pod.
| Example check | Evidence |
|---|---|
| Claim bound to the intended volume | PVC VOLUME column names the PV |
| Class and mode match | Same storageClassName and access mode on PV and PVC |
| Workload uses the claim | Pod volume references the claim and the mount path is readable |
Cleanup: kubectl delete namespace prepium-tutorial, then kubectl delete pv archive-pv (a PV is not namespaced, so deleting the namespace leaves it behind). Reference: Persistent Volumes. Next, try StorageClasses and dynamic provisioning.
What this tests
Configure persistent volumes, claims, storage classes, and volume access modes. On the CKA exam, Storage tasks are graded purely on what you build in the cluster - not multiple choice - so the only way to get faster is to do them on a real cluster against a clock.
Practice it for real
The public description lets you study the task before signing up. Full access includes every live lab. Six selected CKA scenarios are included in Free Practice, with five shared sessions each week. You solve the task in a real terminal, hit validate, and a programmatic checker scores exactly what you got right and wrong (with partial credit). You can open the solution while practising, then retry and use the failed checks to improve your score.