Skip to content

PersistentVolumes, Claims, and StorageClasses

A PersistentVolumeClaim is a request for storage that a Pod mounts, and it binds to a PersistentVolume — a piece of cluster storage that can outlive the Pod and follow it across nodes.

The gap emptyDir and hostPath cannot close

Section titled “The gap emptyDir and hostPath cannot close”

emptyDir dies with the Pod, and hostPath ties data to one specific node — neither survives a Pod being rescheduled onto different hardware, which is exactly what the scheduler is free to do at any time. Real stateful workloads (databases, message queues, anything with data that matters) need storage that exists independently of any single Pod or node, and that a rescheduled Pod can reattach to wherever it lands.

PersistentVolume and PersistentVolumeClaim

Section titled “PersistentVolume and PersistentVolumeClaim”

A PersistentVolume (PV) is a cluster-level storage resource — conceptually similar to how a Node represents compute capacity, a PV represents a chunk of storage capacity. A cluster administrator can provision PVs statically ahead of time, or they can be created dynamically (more on that below).

A PersistentVolumeClaim (PVC) is a request made by a user or application: “I need this much storage, with this access mode.” Kubernetes binds the PVC to a PV that satisfies the request. Crucially, Pods reference the PVC, never the PV directly — the Pod does not need to know or care which physical PV it ended up bound to.

apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-manual-10gi
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: manual
hostPath:
path: /mnt/data
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: data-claim
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
storageClassName: manual
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-claim

A PV declares which access modes it supports, and a PVC requests one it needs:

  • ReadWriteOnce (RWO) — the volume can be mounted read-write, but only by Pods running on a single node at a time.
  • ReadOnlyMany (ROX) — the volume can be mounted read-only by many nodes at once.
  • ReadWriteMany (RWX) — the volume can be mounted read-write by many nodes at once (only some storage backends support this).
  • ReadWriteOncePod (RWOP) — a newer, stricter mode: the volume is restricted to a single Pod across the whole cluster, not just a single node. Plain ReadWriteOnce still allows multiple Pods to share the volume as long as they land on the same node; ReadWriteOncePod closes that gap when you need a true single-writer guarantee.

Hand-provisioning PVs does not scale. A StorageClass describes a class of storage and a provisioner capable of creating it on demand — when a PVC references a StorageClass instead of an existing PV, a CSI (Container Storage Interface) driver automatically provisions a matching PV for it. No administrator has to pre-create anything.

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast-ssd
provisioner: ebs.csi.aws.com
parameters:
type: gp3
reclaimPolicy: Delete
volumeBindingMode: WaitForFirstConsumer
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: dynamic-claim
spec:
accessModes:
- ReadWriteOnce
storageClassName: fast-ssd
resources:
requests:
storage: 20Gi
Terminal window
# Watch a PVC bind to a dynamically provisioned PV
kubectl get pvc dynamic-claim -w

Reclaim policy: what happens after the PVC is gone

Section titled “Reclaim policy: what happens after the PVC is gone”

The reclaim policy decides the fate of the underlying storage once its PVC is deleted:

  • Delete — the PV and its underlying storage (the actual disk) are deleted along with the PVC. This is the default for most dynamically provisioned StorageClasses.
  • Retain — the PV and its data are kept around after the PVC is deleted, so an administrator can manually recover or clean up the data before the underlying storage is released.
flowchart LR
  pod["Pod"] -->|mounts| pvc["PVC: dynamic-claim"]
  pvc -->|references| sc["StorageClass: fast-ssd"]
  sc -->|CSI driver provisions| pv["New PersistentVolume"]
  pv --> disk[("Underlying disk, e.g. an EBS volume")]
Dynamic provisioning: a PVC triggers a StorageClass and CSI driver to create a new PV
When a Pod needs persistent storage, what does it reference in its spec.volumes
What does a StorageClass let you avoid doing
What does the ReadWriteOncePod access mode restrict that plain ReadWriteOnce does not
What is the practical difference between a Delete and a Retain reclaim policy