A Kubernetes PersistentVolume (PV) is cluster storage that outlives pods, per the K8s docs. PersistentVolumeClaims (PVCs) request storage. StorageClasses enable dynamic provisioning (the cluster creates PVs on-demand). The team that uses dynamic provisioning has storage without manually creating PVs. The team that uses StatefulSets with stable PVCs has pods that get the same storage across restarts.
Table of contents
- PV vs PVC
- Static PV example
- PVC example
- StorageClass for dynamic provisioning
- Using PVC in pod
- StatefulSet with stable PVCs
- Reclaim policies
- FAQ
PV vs PVC
PV (PersistentVolume): cluster-wide storage resource (admin creates). PVC (PersistentVolumeClaim): namespace-scoped request for storage (developer creates).
The team that has admin-managed PVs and developer-managed PVCs has the right separation. PVC binds to PV based on size, access mode, and storage class.
Static PV example
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-data
spec:
capacity:
storage: 100Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: standard
hostPath:
path: /data/pv-data
The team that has static PVs manually binds PVCs to them. More work but full control.
PVC example
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: data-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 50Gi
storageClassName: standard
The team that creates PVCs has K8s bind them to matching PVs. With dynamic provisioning (StorageClass), the PV is created on-demand.
StorageClass for dynamic provisioning
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast-ssd
provisioner: ebs.csi.aws.com # AWS EBS
parameters:
type: gp3
fsType: ext4
reclaimPolicy: Delete
volumeBindingMode: WaitForFirstConsumer
The team that uses StorageClass has dynamic PV creation on PVC request. AWS EKS uses ebs.csi.aws.com, GKE uses pd.csi.storage.gke.io, Azure uses disk.csi.azure.com.
Using PVC in pod
apiVersion: v1
kind: Pod
metadata:
name: app
spec:
containers:
- name: app
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
persistentVolumeClaim:
claimName: data-pvc
The team that mounts PVCs in pods has persistent storage.
StatefulSet with stable PVCs
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: db
spec:
serviceName: db
replicas: 3
template:
spec:
containers:
- name: db
volumeMounts:
- name: data
mountPath: /var/lib/data
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: fast-ssd
resources:
requests:
storage: 100Gi
volumeClaimTemplates creates a PVC per replica (db-0, db-1, db-2). The team that uses StatefulSets for databases has stable per-replica storage.
Reclaim policies
- Retain: PVC deleted, PV remains (manual cleanup).
- Delete: PVC deleted, PV + underlying storage deleted.
- Recycle: deprecated.
The team that uses Retain for production has manual control over data. The team that uses Delete has automatic cleanup but data is gone on PVC delete.
FAQ
What’s the difference between PV and PVC?
PV = actual storage. PVC = request for storage. K8s binds PVCs to matching PVs.
Can I resize a PVC?
Yes if the storage class allows it. Edit the PVC’s spec.resources.requests.storage. The team that has dynamic provisioning with allowVolumeExpansion: true has resize support.
What’s a StorageClass?
A template for dynamic provisioning. Defines the provisioner (cloud), parameters (type, IOPS), and reclaim policy.
Can I use NFS with PVs?
Yes - NFS-based PVs work. Or use the CSI driver for managed NFS (AWS EFS, GCP Filestore).
What happens if I delete a pod with a PVC?
PVC persists. The data survives. New pod can claim the same PVC. The team that uses StatefulSets has the same PVC bound to the same pod ordinal across restarts.
If you are sizing the infrastructure for the kind of project this post covers, the RunxBuild hosting calculator is the right place to model the line items. The compute, the memory, the storage, the bandwidth, the database - each one is a separate number, and the team’s mental model for the platform is the sum of those numbers. The RunxBuild dashboard is where the team sees the actual usage in one place.
Useful related references: