The Exam Blueprint · CKA · D4 · Storage · 10%

Storage

Domain 4 of the CKA curriculum is the smallest by weight — 10%, tied for last — and the most mechanical: three published competencies, no sprawling sub-topics, no shifting vocabulary between exam versions. It asks whether you can make a Pod's data outlive the Pod that wrote it, using exactly three cooperating objects — a StorageClass that describes a kind of storage and how to make more of it on demand, a PersistentVolumeClaim that asks for some, and a PersistentVolume that is the actual piece of storage once something has bound the two together. Small weight, real cost if you skip it: a StatefulSet with a misconfigured access mode, a PVC quietly stuck Pending forever, or a reclaim policy that either deletes data you needed or leaves an orphaned disk racking up cloud spend — all of that lives in this ten percent. This page covers the object model, the exact fields the exam probes, and a diagnostic sequence for the single most common storage failure: a claim that refuses to bind.

☺ Explain it like I'm 10

Think of a self-storage facility. The front desk keeps a catalog of unit types — small, climate-controlled, drive-up — and for each type a standing instruction: "when someone asks for one of these and we're out, build another." That catalog is the StorageClass. You don't walk up and grab a unit yourself — you fill out a request form saying what size you need and how many people should get a key. That form is your PersistentVolumeClaim. The front desk matches your form to an actual numbered unit, or builds a fresh one if the catalog says it can — that assigned unit is the PersistentVolume. And when you move out, the standing instruction on your form decides what happens next: keep my stuff for me, or clear it out and reuse the unit for the next customer. Forget to check that instruction, and you either lose a box you needed or pay rent on a unit nobody remembers renting.

🐘🦫Your hosts for this topic: Ellie the Elephant & Benny the Beaver — Ellie never drops a byte of state, so the durable-storage half of the cluster is her department; Benny writes the manifests that actually claim it.

The domain: where 10% of the exam goes

☺ Like you're 10: Five topics make up the whole exam, and they're not equal sizes — Storage is the smallest slice, but it still has to be studied on its own.

These are the five CKA domains and weights exactly as published in the CNCF's Certified Kubernetes Administrator (CKA) Exam Curriculum, version 1.35. They sum to exactly 100% — 25 + 15 + 20 + 10 + 30 — and carry 27 published competencies between them. This page is Domain 4; the other four are linked below.

Storage publishes exactly three competencies: implement storage classes and dynamic volume provisioning; configure volume types, access modes and reclaim policies; manage persistent volumes and persistent volume claims. No more, no fewer — if a source quotes a fourth Storage bullet about troubleshooting clusters and nodes, that's a copy-paste artifact of the curriculum PDF's two-column layout, not a real fourth competency. Small as it is, don't skip it for Troubleshooting: a real chunk of D5's 30% is diagnosing broken storage, and you can't troubleshoot what you never learned to configure correctly the first time.

StorageClasses & dynamic provisioning

☺ Like you're 10: A StorageClass is the standing instruction that lets the cluster manufacture a new storage unit the instant someone asks for one, instead of making them wait for a human to build it by hand.

Before dynamic provisioning, a cluster administrator pre-created a pool of PersistentVolumes and hoped the sizes matched what applications would eventually ask for — static provisioning, still valid and still tested, but tedious at any real scale. A StorageClass flips that: it names a provisioner (a CSI driver, almost always — see Storage & the CSI for how that driver actually talks to the underlying disk system) and a set of parameters that provisioner understands, and the cluster creates a brand-new PersistentVolume on demand the moment a claim asks for that class.

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast-ssd
  annotations:
    storageclass.kubernetes.io/is-default-class: "true"   # at most one class should carry this
provisioner: ebs.csi.aws.com          # a CSI driver name — cloud-specific, exam-agnostic in shape
parameters:
  type: gp3
  iops: "3000"
reclaimPolicy: Delete                 # the StorageClass default if the PV omits its own
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true

Four fields on that object are worth knowing cold. reclaimPolicy sets the default a dynamically provisioned PersistentVolume inherits — almost always Delete unless the class overrides it, which is the opposite of what most people assume. volumeBindingMode decides when binding and provisioning happen: Immediate binds as soon as the PVC exists, which on a multi-zone cluster can provision a disk in a zone no eligible node can reach; WaitForFirstConsumer delays binding until a Pod actually claims the volume, so the scheduler's node choice and the disk's zone are guaranteed to agree — the exam's default-correct answer for anything topology-aware. allowVolumeExpansion: true is what makes kubectl edit pvc with a larger storage request actually grow the underlying disk instead of being silently ignored. And exactly one StorageClass in a cluster should carry the is-default-class: "true" annotation — a PVC that names no storageClassName at all uses whichever class holds it, and two classes claiming default at once is a cluster misconfiguration, not a feature.

kubectl get storageclass                       # SC abbreviates it; look for (default) on one row
kubectl describe storageclass fast-ssd          # provisioner, parameters, reclaimPolicy, bindingMode
kubectl get storageclass -o \
  jsonpath='{.items[?(@.metadata.annotations.storageclass\.kubernetes\.io/is-default-class=="true")].metadata.name}'

Volume types, access modes & reclaim policy

☺ Like you're 10: Not every volume needs to survive the Pod — some are just scratch space that vanishes with it — and every durable one has to say how many Pods, and in what direction, are allowed to touch it at once.

Kubernetes' volumes field on a Pod spec covers far more than persistent disks. Ephemeral types live and die with the Pod: emptyDir is scratch space wiped the moment the Pod is removed from a node (useful for a sidecar sharing files with its main container), configMap and secret volumes project cluster config into a container's filesystem, and hostPath mounts a path from the node itself — powerful, and one of the riskier objects a NetworkPolicy or PodSecurity admission rule exists to restrict. This domain's actual subject is the durable path: a volume backed by a PersistentVolume, provisioned by a CSI driver, that outlives any single Pod.

Every PersistentVolume declares one or more access modes, and a PersistentVolumeClaim requesting a mode the volume doesn't offer will never bind:

Access modeMeaning
ReadWriteOnce (RWO)Read-write by one node at a time — the common case for a block-storage disk like an EBS or Persistent Disk volume. Multiple Pods on the same node can still share it.
ReadOnlyMany (ROX)Read-only, mountable by many nodes at once — a shared, static dataset several Pods only read from.
ReadWriteMany (RWX)Read-write by many nodes at once — needs a genuinely networked filesystem (NFS, EFS, Azure Files, CephFS); most block-storage CSI drivers can't offer it.
ReadWriteOncePod (RWOP)Read-write, and stricter than RWO: exactly one Pod in the whole cluster, not one per node. Added to guarantee single-writer semantics for CSI drivers where two Pods on one node sharing an RWO volume would corrupt state.

A volume's reclaim policy decides what happens to the underlying storage after the PVC that bound it is deleted. Delete — the dynamic-provisioning default — removes the PersistentVolume object and asks the CSI driver to delete the backing disk: fast to clean up, unforgiving if the claim was deleted by mistake. Retain keeps both the PV object and the underlying disk around, but the PV moves to a Released phase and won't automatically bind to a new claim — an administrator has to manually clear its claimRef before anything can reuse it. Recycle is deprecated; don't reach for it. On a StatefulSet, don't assume PVCs vanish when the workload does: by default they outlive both Pod deletion and the StatefulSet's own deletion — deliberately, so a redeploy doesn't silently drop a database's disk — unless the StatefulSet sets a persistentVolumeClaimRetentionPolicy telling Kubernetes to delete them onStatefulSetDeletion or onScaledown.

⚠ Watch out

Dynamically provisioned volumes default to reclaimPolicy: Delete — deleting the PVC really does delete the disk, cloud bill and all. If a workload's data must survive a claim deletion, either set reclaimPolicy: Retain on the StorageClass before anything is provisioned from it (changing it afterward doesn't retroactively update volumes already created), or patch the individual PersistentVolume's persistentVolumeReclaimPolicy field directly once you know it holds something worth keeping.

PersistentVolumes & PersistentVolumeClaims

☺ Like you're 10: The claim is the request, the volume is the actual storage, and something in the middle has to match one to the other before a Pod can ever mount it.

A PersistentVolume (PV) is a cluster-scoped object representing a real piece of storage — capacity, access modes, and the driver that speaks to it. A PersistentVolumeClaim (PVC) is a namespaced request for some storage matching a size, an access mode, and (usually) a StorageClass. Binding is a matching process, not a creation process for the claim: the control plane looks for a PV that satisfies the PVC's requested size, access modes, and class, and links the two with a one-to-one claimRef. When the StorageClass names a provisioner and volumeBindingMode allows it, that PV doesn't have to exist yet — the provisioner creates it on the spot to satisfy the claim.

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: reports-data
  namespace: shop
spec:
  accessModes: [ReadWriteOnce]
  storageClassName: fast-ssd
  resources:
    requests:
      storage: 20Gi
---
apiVersion: v1
kind: Pod
metadata:
  name: report-writer
  namespace: shop
spec:
  containers:
    - name: writer
      image: acme/report-writer:v3
      volumeMounts:
        - name: data
          mountPath: /var/lib/reports
  volumes:
    - name: data
      persistentVolumeClaim:
        claimName: reports-data       # the Pod references the claim, never the PV directly

Notice the Pod never names a PersistentVolume — only the claim. That indirection is the whole point: an application manifest stays portable across clusters with completely different underlying storage, because it only ever asks for "20Gi, read-write-once, fast-ssd class," and whatever cluster it lands in supplies a matching volume however that cluster knows how.

Dynamic provisioning, start to finish Pod mounts via claimName PersistentVolumeClaim 20Gi · RWO · fast-ssd namespace: shop StorageClass fast-ssd · provisioner PersistentVolume the actual 20Gi disk names provisions bound — one-to-one claimRef

Diagnosing a Pending PVC

☺ Like you're 10: When a claim just sits there instead of getting matched to storage, the reason is almost always sitting in plain English in its own event log — read that before you guess.

A PersistentVolumeClaim stuck in phase Pending is one of the most common storage tasks on the exam, and it has a short, memorizable list of causes. Always start the same way — describe the claim and read its events, which name the failure directly far more often than any other object in Kubernetes:

kubectl get pvc reports-data -n shop                 # STATUS column: Pending
kubectl describe pvc reports-data -n shop | tail -15  # Events: at the very bottom, plain English

# fastest single check when no default StorageClass exists
kubectl get storageclass

# is a matching PersistentVolume even eligible, for static provisioning?
kubectl get pv -o wide

# check for a provisioner that simply isn't running
kubectl get pods -n kube-system | grep -i csi
kubectl logs -n kube-system -l app=ebs-csi-controller -c csi-provisioner --tail=40

Five causes cover almost every real case, roughly in order of how often they actually show up:

Pending PVC — the check order 1. StorageClass resolves? exists? one marked default? no → fix name or set a default class 2. Binding mode WaitForFirstConsumer, no Pod yet? yes → expected, check for a Pod 3. A matching PV size, access mode, class all line up? no → create or resize a PV 4. Provisioner CSI controller Pod healthy? RBAC ok? no → check logs, ServiceAccount Read the PVC's own events first — most of these are named directly, in plain English, at the bottom of kubectl describe
◆ Key idea

A Pending PVC and a Pending Pod that references it are usually two separate diagnoses. The PVC can bind cleanly and the Pod still sit Pending afterward — most often a WaitForFirstConsumer volume that provisioned into a zone the scheduler can't reach for that Pod. Always describe both objects; don't assume one status explains the other.

🐘 Ellie's workshop · 20 min

Create a PVC naming a storageClassName that doesn't exist and confirm it sits Pending with a clear event. Fix it by pointing the PVC at a real class, or by creating a matching StorageClass. Then create a second PVC against a class using volumeBindingMode: WaitForFirstConsumer with no Pod yet, confirm it's also Pending, and watch it bind the instant you create a Pod that mounts it. Two different roads to the same status — the point is learning to tell them apart from the events alone, without a hint.

🦆 Dot's-eye view

"I don't want to know what a CSI driver is. I want my Deployment's YAML to say 20 gigs, ReadWriteOnce, done — and I want it to work identically whether we're on this cloud or the next one we migrate to. That's the entire pitch of the PVC to me: I describe what I need once, in words that don't change, and somebody else's StorageClass translates it into whatever disk actually exists underneath. The day that abstraction leaks is the day I have to learn what a provisioner is, and I'd really rather not."

This page only covers what D4 tests; the mechanics of how a CSI driver actually attaches, formats and mounts a volume — the plugin architecture Kubernetes delegates all of this to — are unpacked in Storage & the CSI, and stateful application patterns built on top of PVCs (databases, queues, anything that needs one stable disk per replica) live in Stateful Workloads & Database Operators. Platform Engineering's Storage & Stateful Workloads deep dive covers the same PV/PVC/CSI model one altitude up, from the perspective of a platform team offering storage as a self-service API rather than a single cluster administrator provisioning it by hand.

⚠ Verify this before you book

This page states the CKA's format as performance-based, 2 hours, on live clusters, graded on the end state — accurate as commonly published, but exam duration, pricing, question composition and permitted-documentation rules are all things the Linux Foundation and CNCF change over time. This is an independent, unofficial study resource, not affiliated with the CNCF or Linux Foundation. Confirm current logistics on the official Linux Foundation CKA page and the CNCF certification page before you register, and check the curriculum itself at github.com/cncf/curriculum against the version you're studying.

🎬 At the Pod Squad
🦫

Benny: My PVC has been Pending for ten minutes. I've re-applied it three times.

🐘

Ellie: Re-applying an unbound claim doesn't do anything new — it's the same request either way. What do its events actually say?

🦫

Benny: "waiting for a volume to be created" — over and over.

🤖

Recon: That phrasing means the provisioner accepted the job and hasn't finished it — not that nothing's resolving. Check the CSI controller Pod, not the PVC.

👺

Gizmo: Or just switch the StorageClass to Immediate binding and set the reclaim policy to Retain everywhere "to be safe." Sounds cautious. 🤑

🐢

Timmy: "To be safe" isn't a reason to abandon WaitForFirstConsumer — you'd just trade a Pending PVC for a Pending Pod in the wrong zone instead. And blanket Retain means every deleted claim leaves an orphaned disk somebody has to notice and clean up by hand.

🐘

Ellie: Found it — the CSI controller Pod's been crash-looping for eleven minutes. Nothing about your PVC was ever wrong, Benny.

🦫

Benny: ...I checked everything except the one Pod actually doing the work.

🐢 Timmy's checkpoint

1. What are the three objects involved in dynamic provisioning, and what role does each one play? 2. What's the difference between volumeBindingMode: Immediate and WaitForFirstConsumer, and why does the exam favor the second on a multi-zone cluster? 3. Name the four access modes and what each one actually permits. 4. What reclaim policy do dynamically provisioned volumes get by default, and what's the practical risk of leaving it there? 5. List at least three distinct reasons a PVC can be stuck Pending. 6. Why can a PVC bind successfully while the Pod that mounts it still sits Pending?

Check your answers
  1. A StorageClass names a provisioner and how to configure new volumes from it; a PersistentVolumeClaim is the namespaced request for storage matching a size, access mode and class; a PersistentVolume is the actual storage, created on demand by the provisioner and bound one-to-one to the claim that triggered it.
  2. Immediate binds and provisions as soon as the PVC exists, before any Pod is scheduled — on a multi-zone cluster that can create a disk in a zone no eligible node can reach. WaitForFirstConsumer delays both until a Pod actually references the claim, so the volume provisions in the same zone the scheduler already placed that Pod in.
  3. ReadWriteOnce (RWO) — read-write by one node at a time; ReadOnlyMany (ROX) — read-only by many nodes; ReadWriteMany (RWX) — read-write by many nodes, needs a networked filesystem; ReadWriteOncePod (RWOP) — read-write by exactly one Pod cluster-wide, stricter than RWO.
  4. Delete — deleting the PVC deletes the PersistentVolume object and the underlying disk. The practical risk is real data loss from an accidental claim deletion; the fix is setting reclaimPolicy: Retain on the StorageClass before volumes are provisioned, or patching an individual PV afterward.
  5. Any three of: no StorageClass resolves (missing name, or no default set); WaitForFirstConsumer genuinely waiting on a Pod (expected, not a failure); no PersistentVolume matches size/access mode/class in static provisioning; the CSI provisioner itself is unhealthy or missing RBAC; a topology mismatch under Immediate binding.
  6. Because the PVC and the Pod are matched against different constraints — the claim only needs a PV that satisfies size, access mode and class, while the Pod also needs a node the scheduler can actually place it on. A volume provisioned in the wrong zone under Immediate binding satisfies the claim but leaves the Pod unschedulable, and that failure only shows up in the Pod's own events.

Storage is the smallest domain; the largest is next. Continue to Troubleshooting for the 30% that ties every domain together, or revisit Servicing & Networking if Domain 3 is still fresh.