StorageClasses and dynamic provisioning
Creating PVs by hand does not scale: someone has to guess sizes in advance and keep a pile of disks ready. With a StorageClass, you just create a PVC, and Kubernetes asks your cloud or storage system to make a brand-new disk of exactly that size, on the spot. This is dynamic provisioning, and it is what almost every real cluster uses.
Static vs dynamic
Static (chapter 31)
Admin creates PVs first → your PVC binds to one of them. Sizes are whatever the admin guessed. Nothing fits? Pending.
Dynamic (this chapter)
Your PVC names a StorageClass → a disk of exactly the requested size is created for you → a PV is created and bound automatically. No admin in the loop.
What happens on apply
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: posts-data spec: storageClassName: fast-ssd accessModes: [ReadWriteOnce] resources: requests: storage: 20GiYou only write the PVC, naming a StorageClass.
$ kubectl get pvc NAME STATUS STORAGECLASS posts-data Pending fast-ssd ← waiting for a Pod (WaitForFirstConsumer)Many classes wait until a Pod actually uses the claim, so the disk is created in the same zone as the node the Pod is scheduled on.
Pod posts-0 scheduled to node-2 (zone eu-west-1b) provisioner ebs.csi.aws.com: create 20Gi gp3 disk in eu-west-1b → vol-0a1b2c3d createdThe class's provisioner, a CSI driver running in your cluster, calls the cloud API and creates a real disk.
$ kubectl get pv NAME CAPACITY STATUS CLAIM pvc-6f1c2e9a-4b7d-4c1e-9a2f-0d3e5b8c7a11 20Gi Bound default/posts-dataA PV is created automatically (named after the claim's ID) and bound. The disk is attached to node-2 and mounted into the Pod.
A StorageClass
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast-ssd
provisioner: ebs.csi.aws.com
parameters:
type: gp3
encrypted: "true"
reclaimPolicy: Delete
allowVolumeExpansion: true
volumeBindingMode: WaitForFirstConsumer
provisioner
Which driver creates the disks. Examples: ebs.csi.aws.com (AWS), pd.csi.storage.gke.io (Google), disk.csi.azure.com (Azure), rancher.io/local-path (k3s, kind), k8s.io/minikube-hostpath (minikube).
parameters
Passed straight to the provisioner; each one has its own. Here: AWS gp3 SSDs, encrypted. The point of having several classes: fast-ssd, cheap-hdd, shared-nfs…
reclaimPolicy
What happens to the disk when the PVC is deleted. Default Delete. See below.
allowVolumeExpansion
Whether PVCs of this class may be made bigger later.
volumeBindingMode
Immediate: create the disk as soon as the PVC appears. WaitForFirstConsumer: wait for a Pod, so the disk ends up in the right zone. Prefer the second in multi-zone clusters; a disk in zone A cannot attach to a node in zone B.
The default class
$ kubectl get storageclass
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION
standard (default) k8s.io/minikube-hostpath Delete Immediate false
fast-ssd ebs.csi.aws.com Delete WaitForFirstConsumer true
| In your PVC | Result |
|---|---|
No storageClassName at all | The default class is used. This is why a plain PVC "just works" on minikube and cloud clusters. |
storageClassName: fast-ssd | That class. |
storageClassName: "" | No dynamic provisioning: bind only to a hand-made PV (static). |
Reclaim policies
You delete the PVC. What happens to the disk and the data?
PVC deleted → PV deleted → cloud disk deleted. Data gone.The default for dynamic provisioning. Tidy, no forgotten disks costing money, but one kubectl delete destroys the data. Fine for caches and test environments.
PVC deleted → PV becomes Released → disk and data kept.Safe for anything important: a database disk survives a mistaken delete. An admin later recovers the data or deletes the PV and disk by hand. Use a Retain class for production databases, or change an existing PV:
$ kubectl patch pv pvc-6f1c… -p '{"spec":{"persistentVolumeReclaimPolicy":"Retain"}}'PVC deleted → files wiped (rm -rf) → PV Available again.Deprecated. Only ever worked for NFS and hostPath, and a simple wipe is not secure. You will see it in old tutorials; use dynamic provisioning with Delete instead.
Growing a volume
The database is running out of space. If the class has allowVolumeExpansion: true, just ask for more:
$ kubectl patch pvc posts-data -p '{"spec":{"resources":{"requests":{"storage":"50Gi"}}}}' persistentvolumeclaim/posts-data patchedOr edit
storage: 50Giin your YAML and apply.$ kubectl describe pvc posts-data Conditions: Type Status FileSystemResizePending TrueThe provisioner grows the cloud disk first. Then the file system on it must grow too.
$ kubectl get pvc posts-data NAME STATUS CAPACITY posts-data Bound 50GiMost CSI drivers resize the file system while the Pod runs. Some older ones need the Pod restarted.
Recap
- A StorageClass describes a kind of storage and which provisioner creates it.
- Dynamic provisioning: write a PVC, get a new disk and PV automatically.
- No
storageClassName→ the default class;""→ static only. WaitForFirstConsumercreates disks in the zone where the Pod runs.- Reclaim:
Delete(default, data gone),Retain(keep, clean up by hand),Recycle(deprecated). - With
allowVolumeExpansion, raise the PVC size to grow a volume. Never shrink.
Quick quiz
- Production PostgreSQL data
A mistaken
kubectl delete pvcmust not destroy it. - Per-branch preview environments, created and destroyed daily
With Retain you would pile up hundreds of paid, orphaned disks.
- A build cache that can be regenerated
Losing it only costs a slower build.
StorageClasses: common questions
What is the difference between Retain and Delete reclaim policies?
With Delete, deleting the PVC also deletes the PV and the disk. With Retain, the PV is kept as Released with its data, and an admin cleans it up.