Kubernetes Storage: Volumes, PersistentVolumes, PVCs, and CSI
Kubernetes storage is a layered answer to one problem: a container's filesystem is ephemeral, so anything written inside it vanishes when the container is replaced. Volumes attach storage to a Pod for the Pod's lifetime; PersistentVolumes (PV) and PersistentVolumeClaims (PVC) decouple storage from any single Pod so data survives rescheduling; StorageClasses add dynamic provisioning so the cluster creates storage on demand; and the Container Storage Interface (CSI) is the standard plugin API that lets any storage vendor supply the actual disks. KCNA tests this stack at recognition depth: which object requests storage versus supplies it, what the four access modes permit, what Retain and Delete reclaim policies do, and how a StatefulSet gives each replica its own claim. This lesson works up the stack layer by layer - ephemeral volumes, the PV/PVC binding model, access modes and reclaim policies, StorageClasses, CSI, and finally stateful workloads - so each acronym lands as a concrete role.
On this page8 sections
- Why Kubernetes separates storage from Pods
- Ephemeral volumes: emptyDir, configMap, secret, and hostPath
- PersistentVolumes and PersistentVolumeClaims: supply meets demand
- Access modes: what ReadWriteOnce actually means
- Reclaim policies: what happens to data when the claim goes away
- StorageClasses and dynamic provisioning
- The Container Storage Interface (CSI)
- Stateful workloads: StatefulSets and volumeClaimTemplates
- Explain why container filesystems are ephemeral and how volumes address it
- Identify what emptyDir, configMap, secret, and hostPath volumes are used for
- Describe the PersistentVolume and PersistentVolumeClaim model and how claims bind to volumes
- Name the four access modes and state exactly what ReadWriteOnce permits
- Contrast the Retain and Delete reclaim policies and when each applies
- Explain what StorageClasses, dynamic provisioning, and CSI each contribute, and how StatefulSets use volumeClaimTemplates
Why Kubernetes separates storage from Pods
A container's writable filesystem lives exactly as long as the container. When a container crashes and the kubelet restarts it, the replacement starts from the image again and everything written at runtime is gone. Pods add a second cliff: when a Pod is deleted or rescheduled to another node, even storage tied to the Pod disappears with it. Any real application - a database, a message queue, anything with user data - needs its data to outlive both events, and that is the problem the Kubernetes storage stack solves in layers.
The layers are worth naming up front, because every storage question on the exam is really asking which layer does what. A Volume is storage declared in a Pod spec and mounted into its containers; it outlives container restarts but, for the ephemeral types, not the Pod itself. A PersistentVolume (PV) is a piece of storage that exists as a cluster resource in its own right, independent of any Pod. A PersistentVolumeClaim (PVC) is a request for such storage that a Pod consumes by name. A StorageClass describes a type of storage the cluster can create on demand, enabling dynamic provisioning. And the Container Storage Interface (CSI) is the plugin standard through which external storage drivers actually attach and mount the disks.
| Layer | Object | Role | Lifetime |
|---|---|---|---|
| In the Pod | Volume | Mountable storage declared in the Pod spec | Ephemeral types die with the Pod |
| Cluster resource | PersistentVolume | The supplied storage itself | Independent of any Pod |
| Namespaced request | PersistentVolumeClaim | A request that binds to a PV | Until the claim is deleted |
| Provisioning template | StorageClass | Recipe for creating PVs on demand | Cluster configuration |
| Driver interface | CSI | Standard API for storage vendors | Cluster infrastructure |
Ephemeral volumes: emptyDir, configMap, secret, and hostPath
Not all storage needs to persist. Kubernetes ships several volume types whose lifetime is deliberately tied to the Pod, and the exam expects you to recognize what each is for.
An emptyDir volume is created empty when the Pod is assigned to a node and deleted when the Pod is removed. It survives container crashes and restarts within the Pod, which makes it the standard scratch space: caches, temporary processing files, and - importantly - a shared directory between containers in the same Pod, such as an app writing files that a sidecar ships elsewhere. A configMap volume projects the keys of a ConfigMap as read-only files inside the container, the usual way to deliver configuration files without baking them into the image. A secret volume does the same for Secrets - credentials, TLS certificates - mounted as files rather than injected as environment variables. A hostPath volume mounts a directory from the node's own filesystem into the Pod. It has legitimate niche uses, mostly for node-level agents that need to read host logs or sockets, but it couples the Pod's data to one specific node and opens access to the host, so it is flagged as a security risk and avoided for ordinary applications.
| Volume type | Backed by | Typical use | Caveat |
|---|---|---|---|
| emptyDir | Node disk or memory | Scratch space; sharing files between containers in a Pod | Deleted when the Pod goes away |
| configMap | A ConfigMap object | Configuration files | Read-only projection |
| secret | A Secret object | Credentials and certificates as files | Read-only projection |
| hostPath | The node's filesystem | Node agents reading host paths | Node-tied and a security risk |
The common thread: none of these give an application durable data. For that, Kubernetes switches from volumes declared inline in a Pod to storage managed as its own resource - the subject of the next section.
PersistentVolumes and PersistentVolumeClaims: supply meets demand
Kubernetes splits persistent storage into two objects on purpose. A PersistentVolume is the supply side: a cluster-scoped resource representing an actual piece of storage - a cloud disk, an NFS export, a SAN LUN - with a capacity, access modes, and a reclaim policy. PVs are created by an administrator in advance (static provisioning) or by the cluster on demand (dynamic provisioning). A PersistentVolumeClaim is the demand side: a namespaced object in which an application requests storage by size, access mode, and optionally StorageClass, without knowing or caring what technology sits underneath. The separation is the same consumer/provider split as Pods and nodes: developers ask for what they need, infrastructure supplies it.
The two meet through binding. The control plane matches a pending PVC to a suitable PV - enough capacity, compatible access mode, matching class - and binds them one-to-one; a bound PV belongs to exactly one claim. The Pod then consumes the claim by name:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: data-claim
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
---
apiVersion: v1
kind: Pod
metadata:
name: db
spec:
containers:
- name: postgres
image: postgres:16
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
volumes:
- name: data
persistentVolumeClaim:
claimName: data-claimNotice what the Pod references: the claim, never the PV. If the Pod dies and is recreated on another node, it mounts the same claim and finds its data intact - persistence is achieved because the storage's lifecycle is anchored to the PVC and PV, not to the Pod. One scoping fact recurs on the exam: PVCs are namespaced, PVs are cluster-scoped.
Access modes: what ReadWriteOnce actually means
Every PV and PVC declares access modes, which describe how the storage can be mounted. There are exactly four, each with a standard abbreviation, and inventing a fifth is a favorite distractor.
| Mode | Abbreviation | What it allows | Typical backing storage |
|---|---|---|---|
| ReadWriteOnce | RWO | Read-write mount by a single node | Cloud block disks (one attachment at a time) |
| ReadOnlyMany | ROX | Read-only mount by many nodes | Shared reference data |
| ReadWriteMany | RWX | Read-write mount by many nodes | File and network storage such as NFS |
| ReadWriteOncePod | RWOP | Read-write mount by a single Pod in the whole cluster | CSI volumes needing exclusive access |
The trap the exam sets is in the word Once. ReadWriteOnce restricts the mount to one node, not one Pod. Several Pods scheduled onto that same node can all use an RWO volume simultaneously. If you genuinely need to guarantee that only one Pod anywhere in the cluster can write, that is ReadWriteOncePod, a newer mode supported only by CSI volumes. Keep the pair straight: RWO = one node, RWOP = one Pod.
Two supporting facts round out the picture. A PV may list several supported access modes, but it is mounted with only one mode at a time. And access modes are a matching constraint during binding: a claim requesting ReadWriteMany can only bind to a PV whose storage actually supports it, which is why RWX is common on NFS-style file storage and generally unavailable on plain cloud block disks. When a scenario mentions many Pods on different nodes writing to one shared volume, the answer is RWX and file or network storage underneath.
Reclaim policies: what happens to data when the claim goes away
Deleting a Pod never deletes its PVC, and deleting a PVC does not automatically mean the data is destroyed - what happens next is decided by the PV's persistentVolumeReclaimPolicy. Two policies matter.
Retain keeps everything. When the claim is deleted, the PV is not removed and its data is untouched; the volume moves to the Released state, but it is not made available to a new claim automatically, because it still contains the previous owner's data. An administrator must intervene - back up or scrub the data, then delete or recreate the PV - before the storage is reused. Retain is the safety-first choice for data you cannot afford to lose to an accidental deletion.
Delete removes the PV object and the underlying storage asset in the external infrastructure - the cloud disk itself is deleted along with the claim. This is the automation-first choice and it is the typical default for dynamically provisioned volumes, where storage was created on demand and can be discarded on demand. The convenience cuts both ways: deleting the wrong PVC can destroy real data, which is exactly why the distinction is exam-worthy.
- Need the data to survive claim deletion, with manual cleanup? Retain.
- Want storage to be created and destroyed automatically with the claim? Delete.
- A third historical policy, Recycle, performed a basic wipe for reuse; it is deprecated, and recognizing it as obsolete is all KCNA requires.
A last vocabulary anchor: a PV's status walks through Available (unbound), Bound (attached to a claim), and Released (claim deleted, awaiting reclaim) - and with Retain, Released is where it waits for a human.
StorageClasses and dynamic provisioning
Static provisioning has an obvious scaling problem: an administrator must guess demand and hand-create PVs ahead of every claim. Dynamic provisioning removes the guesswork. A StorageClass is a cluster-scoped object that describes a kind of storage the cluster can manufacture: which provisioner (storage driver) to call, with what parameters (disk type, performance tier, replication), and what reclaim policy the resulting PVs get. When a PVC names a StorageClass and no suitable PV exists, the provisioner creates a new volume in the backing storage system, wraps it in a PV, and binds it to the claim - supply generated to order.
A cluster typically offers several classes as a menu: a fast SSD class for databases, a cheaper standard class for bulk data, an RWX-capable file storage class for shared volumes. One class can be marked as the default StorageClass, which is applied to any PVC that does not specify one - the reason storage appears by magic on managed clouds: EKS, AKS, and GKE ship a default class wired to their disk services, so a bare PVC just works. A PVC can also opt out of dynamic provisioning entirely by setting an empty class name, forcing a bind to a pre-created PV.
One refinement is worth recognizing: a StorageClass's volumeBindingMode. The default, Immediate, provisions and binds as soon as the claim is created. WaitForFirstConsumer delays provisioning until a Pod actually uses the claim, so the volume can be created in the same availability zone the scheduler picks for the Pod - avoiding the classic failure where a disk exists in zone A and the Pod lands in zone B. For the exam, the core equation is enough: StorageClass plus PVC equals a PV created on demand.
The Container Storage Interface (CSI)
Someone has to actually create the cloud disk, attach it to the right node, format it, and mount it into the Pod. Historically that code lived in-tree: volume plugins for each vendor were compiled into Kubernetes itself, so adding or fixing a storage integration meant changing and re-releasing Kubernetes core. The Container Storage Interface (CSI) ended that. CSI is an open standard API between container orchestrators and storage systems: a vendor implements a CSI driver once, ships it as its own deployable component running in the cluster, and Kubernetes calls the standard interface to provision, attach, mount, and unmount volumes. Drivers now develop and release on their own schedule, out of tree.
You can recognize CSI in the wild by names like ebs.csi.aws.com or pd.csi.storage.gke.io appearing as the provisioner in a StorageClass. The old in-tree cloud volume plugins have been migrated to CSI drivers, and new storage capabilities land in CSI first - features from earlier sections, such as the ReadWriteOncePod access mode and much of volume snapshotting and expansion, exist only through CSI drivers.
For KCNA, place CSI beside its siblings and its role becomes obvious. Kubernetes standardizes its extension points as interfaces: CRI for container runtimes, CNI for networking, and CSI for storage. A question offering those three as options and asking which one storage vendors implement is testing exactly this mapping. CSI is not a volume type you pick in a Pod spec and not a competitor to PVs and PVCs - it is the plumbing layer that StorageClass provisioners and PV attach/mount operations call into.
Stateful workloads: StatefulSets and volumeClaimTemplates
The stack comes together in stateful applications. A Deployment treats its Pods as interchangeable, and if you attach one PVC to a Deployment, every replica tries to share that single claim - unworkable for a database where each member needs its own disk. A StatefulSet solves this with volumeClaimTemplates: a template from which a PVC is stamped out for each replica, paired with the replica's stable identity. A StatefulSet named db with three replicas and a template named data yields Pods db-0, db-1, db-2 and claims data-db-0, data-db-1, data-db-2 - each Pod permanently paired with its own claim.
Walk the full lifecycle for one replica. The StatefulSet creates db-1 and its PVC data-db-1. The PVC names a StorageClass, so the CSI provisioner creates a disk, wraps it in a PV, and binds it. The scheduler places the Pod (with WaitForFirstConsumer, the disk is created in the Pod's zone), the driver attaches and mounts the volume, and the database writes to it. Now the node dies. The Pod is recreated - same name, db-1 - on another node, mounts the same PVC, and finds every byte it wrote. Scaling the StatefulSet down deletes Pods but, by default, keeps their PVCs, so scaling back up reunites each replica with its data; deleting the claims is a deliberate separate act.
That is the whole point of the storage stack in one scenario: the Pod is disposable, the claim endures, the class manufactured the volume, and CSI did the physical work. If you can narrate that chain - and name which object plays which part - you can answer any KCNA storage question by locating it in the chain.
Tip. KCNA probes storage as role recognition: which object requests storage (PVC) versus supplies it (PV), what a StorageClass adds (dynamic provisioning), and which interface vendors implement (CSI, offered beside CRI and CNI as distractors). Expect the access-mode abbreviations RWO, ROX, RWX, and RWOP, with the one-node-versus-one-Pod distinction for ReadWriteOnce as the standard trap, and Retain versus Delete reclaim behavior. Trigger words include emptyDir, hostPath, binding, Released, default StorageClass, and volumeClaimTemplates for StatefulSets.
- A Volume is declared in the Pod spec and (for ephemeral types like emptyDir) lives and dies with the Pod; emptyDir survives container restarts but not Pod deletion.
- PersistentVolumes are the cluster-scoped supply of storage; PersistentVolumeClaims are namespaced requests that bind to them one-to-one, and Pods reference the claim, never the PV.
- The four access modes are ReadWriteOnce (RWO), ReadOnlyMany (ROX), ReadWriteMany (RWX), and ReadWriteOncePod (RWOP).
- ReadWriteOnce limits the volume to one node, not one Pod; only ReadWriteOncePod guarantees a single Pod, and it requires CSI.
- Reclaim policy Retain keeps the PV and its data (status Released, manual cleanup); Delete removes the PV and the underlying storage, and is typical for dynamically provisioned volumes.
- A StorageClass defines a provisioner and parameters; a PVC referencing it triggers dynamic provisioning, and a default class serves PVCs that specify none.
- CSI is the standard interface storage vendors implement, alongside CRI for runtimes and CNI for networking; it replaced in-tree volume plugins.
- StatefulSet volumeClaimTemplates create one PVC per replica (like data-db-0), and those PVCs are kept by default when Pods are rescheduled or scaled down.
Frequently asked questions
What is the difference between a PersistentVolume and a PersistentVolumeClaim?
A PersistentVolume (PV) is the supply side: a cluster-scoped resource representing actual storage such as a cloud disk or NFS export, created by an administrator or by dynamic provisioning. A PersistentVolumeClaim (PVC) is the demand side: a namespaced request in which an application asks for storage by size, access mode, and class. Kubernetes binds a claim to a matching PV one-to-one, and Pods mount storage by referencing the claim, never the PV directly.
Does ReadWriteOnce mean only one Pod can use the volume?
No. ReadWriteOnce (RWO) means the volume can be mounted read-write by a single node, so multiple Pods running on that same node can all use it at once. If you need a guarantee that only one Pod in the entire cluster can mount the volume read-write, that is the separate ReadWriteOncePod (RWOP) access mode, which is only available through CSI volumes. Confusing one node with one Pod is the classic exam trap on access modes.
What happens to my data when I delete a PersistentVolumeClaim?
It depends on the PersistentVolume's reclaim policy. With Retain, the PV and its data are kept: the volume moves to the Released state and an administrator must manually clean it up before the storage can be reused. With Delete, the PV object and the underlying storage asset, such as the cloud disk itself, are removed automatically. Delete is the typical default for dynamically provisioned volumes, so deleting a PVC there really can destroy the data.
What is dynamic provisioning in Kubernetes?
Dynamic provisioning means Kubernetes creates storage on demand instead of an administrator pre-creating PersistentVolumes. A StorageClass defines which provisioner to use and with what parameters; when a PersistentVolumeClaim references that class and no matching PV exists, the provisioner creates a new volume in the backing storage system, wraps it in a PV, and binds it to the claim. A cluster can mark one class as the default, which is applied to any PVC that does not name a class.
What is the Container Storage Interface (CSI)?
CSI is the open standard API between container orchestrators like Kubernetes and storage systems. Storage vendors implement a CSI driver once and run it in the cluster as their own component, and Kubernetes calls the standard interface to provision, attach, mount, and unmount volumes. It replaced the old in-tree volume plugins that had to be compiled into Kubernetes itself. It sits alongside CRI (container runtimes) and CNI (networking) as one of Kubernetes' three main plugin interfaces.
How does a StatefulSet give each replica its own storage?
Through volumeClaimTemplates. The StatefulSet stamps out one PersistentVolumeClaim per replica from the template, named after the replica's stable identity, for example data-db-0 for Pod db-0. Each Pod always mounts its own claim, so a replica rescheduled to another node reattaches to the same data. By default those PVCs are kept even when the StatefulSet scales down, so scaling back up reunites each replica with its previous volume.
Sign up free to mark lessons complete, bookmark topics and track your exam readiness.