Storage: Volumes, PVs, PVCs & StorageClass
A container's own filesystem is ephemeral. Kubernetes Volumes solve this at increasing levels of durability: emptyDir (Pod-lifetime), PersistentVolume/PVC (cluster-scoped, outlives Pods), and StorageC
A container's own filesystem is ephemeral. Kubernetes Volumes solve this at increasing levels of durability: emptyDir (Pod-lifetime), PersistentVolume/PVC (cluster-scoped, outlives Pods), and StorageClass (dynamic provisioning via CSI driver).
-
emptyDir: created when Pod is scheduled, deleted when Pod is removed. Useful for scratch space or sharing files between containers.
-
A PersistentVolume (PV) represents actual storage infrastructure as a cluster-level object with its own lifecycle.
-
A PVC is an application's request for storage (size, access mode) -- Kubernetes binds a matching PV.
-
Access modes: ReadWriteOnce (one NODE at a time, not one Pod), ReadOnlyMany, ReadWriteMany, ReadWriteOncePod.
-
StorageClass enables dynamic provisioning: when a PVC references a StorageClass and no PV exists, the CSI driver creates one.
-
Decide the durability tier needed: emptyDir for Pod-lifetime scratch space, or PersistentVolume/PVC for data that must outlive the Pod.
-
For dynamic provisioning, define or pick a StorageClass (specifies the CSI provisioner, e.g. cloud block storage, and parameters like volume type).
-
Create a PVC requesting size and accessMode; if it references a StorageClass and no matching PV exists, the CSI driver provisions one automatically and binds it.
-
Reference the PVC in the Pod spec's volumes, then mount it into the container via volumeMounts.
-
For StatefulSets, use volumeClaimTemplates instead of a single shared PVC, so each replica gets its own dynamically-provisioned volume.
-
Set the StorageClass's volumeBindingMode: WaitForFirstConsumer when using zonal storage, so the PV is provisioned in the same zone the Pod actually gets scheduled to, not an arbitrary one.
-
A database Pod using a dynamically-provisioned PVC backed by cloud block storage (e.g. EBS, Persistent Disk) via a StorageClass, so data survives Pod rescheduling.
-
Two containers in the same Pod sharing files through an emptyDir volume (e.g. a sidecar processing files an app container writes).
-
A CI build Pod using emptyDir with sizeLimit as scratch space for build artifacts, cleaned up automatically when the Pod exits.
-
A shared team file store using ReadWriteMany-capable storage (like an NFS-backed or cloud file-storage CSI driver) mounted by multiple Pods simultaneously.
-
Multi-zone clusters using WaitForFirstConsumer binding so a Pod and its storage always land in the same availability zone, avoiding cross-zone attach failures.
-
Use WaitForFirstConsumer as the default volumeBindingMode for any StorageClass backing zonal cloud storage -- Immediate binding can provision a volume in a zone the Pod is never scheduled to.
-
Set accessModes deliberately based on actual need -- defaulting to ReadWriteMany when ReadWriteOnce would do limits which storage backends are even viable.
-
Set sizeLimit on emptyDir for anything that could grow unbounded (build caches, logs) to avoid a single Pod exhausting node disk.
-
Use a StorageClass with allowVolumeExpansion: true if you expect PVC sizes to need to grow later -- resizing without it requires manual volume migration.
-
Set persistentVolumeReclaimPolicy: Retain for data you cannot afford to lose to an accidental PVC deletion, rather than the default Delete some provisioners use.
-
Assuming ReadWriteOnce means one Pod at a time -- it actually means one NODE at a time, so multiple Pods on the same node CAN mount the same RWO volume simultaneously.
-
Using emptyDir for data that actually needs to survive Pod restarts -- it's deleted the moment the Pod is removed, not just restarted within limits.
-
Forgetting that a PVC's StorageClass determines binding mode at PVC-creation time -- switching StorageClasses later requires creating a new PVC and migrating data, not editing the existing one.
-
Deleting a PVC without checking persistentVolumeReclaimPolicy first, then being surprised when the underlying cloud disk is gone (Delete policy) rather than retained.
-
Requesting ReadWriteMany without confirming the underlying CSI driver actually supports it -- many common cloud block-storage drivers only support ReadWriteOnce.
-
Not setting a StorageClass at all and relying on the cluster's default, which may have cost or performance characteristics unsuited to the workload.
-
Volume provisioning is not instant -- dynamic provisioning of cloud block storage typically takes seconds to tens of seconds; WaitForFirstConsumer binding avoids double-provisioning delay from a mis-scheduled volume needing to be recreated.
-
Choose StorageClass parameters (volume type, IOPS/throughput tier) matching actual workload I/O patterns -- a database on a low-IOPS StorageClass will bottleneck long before CPU/memory limits are reached.
-
Attach/detach operations for network-backed block storage have real latency on Pod rescheduling -- factor this into failover time expectations, it's not instantaneous like a local disk.
-
emptyDir backed by tmpfs (medium: Memory) is dramatically faster than disk-backed emptyDir for high-churn scratch data, at the cost of consuming node RAM instead of disk.
-
Take regular volume snapshots (via the CSI VolumeSnapshot API, if the driver supports it) independent of application-level backups for stateful workloads' PVCs.
-
Set persistentVolumeReclaimPolicy: Retain plus a documented cleanup process for production data volumes, rather than relying on the provisioner's default Delete behavior.
-
Monitor PVC capacity utilization and alert before a volume fills up -- a full volume causes application-level write failures that don't always surface as an obvious Kubernetes-level event.
-
Standardize on a small number of StorageClasses per cluster (e.g. fast-ssd, standard) rather than letting every team define bespoke ones.
-
Create a StorageClass, a PVC referencing it, and a Pod mounting the PVC; confirm dynamic provisioning by checking
kubectl get pvfor the auto-created PV. -
Delete the Pod (not the PVC) and recreate it, confirming the same PVC/PV reattaches with data intact.
-
Create two containers in one Pod sharing an emptyDir volume -- write a file from one container and read it from the other.
-
Set volumeBindingMode: WaitForFirstConsumer on a StorageClass and observe (via
kubectl get pvc) that the PVC stays Pending until a Pod referencing it is actually scheduled.
-
emptyDir is Pod-lifetime storage; PVs and PVCs provide storage that outlives Pods.
-
PVCs are infrastructure-agnostic requests; StorageClasses define how to dynamically provision matching PVs.
-
ReadWriteOnce restricts to one NODE, not one Pod -- a frequently misunderstood distinction.
-
reclaimPolicy: Retain is safer for important data; Delete is the often-surprising default.
Want a visual for this concept?
Generate a diagram tailored to “Storage: Volumes, PVs, PVCs & StorageClass” — the AI picks whichever visual (flowchart, comparison, sequence, etc.) best fits.
Sign in to generate a visual →