skip to content

StorageClasses and Dynamic Provisioning

A StorageClass names a provisioner and its parameters so a PVC can create its own disk, and volumeBindingMode decides whether that disk is carved out immediately or once the pod is scheduled. Interviewers use it to test zone-aware storage.

part ofKubernetesoverview, primer and where to startread it →
on this pageshow

questions

4

What is a Kubernetes StorageClass, and how does a PersistentVolumeClaim that references one end up with real storage mounted into a Pod?

level: juniorimportance: must knowfreq 68%

answer

  1. Class = provisioner + parameters
  2. Tier name, not a disk name
  3. PVC Pending until provisioner creates PV
  4. Cluster-scoped, no namespace
  5. provisioner/parameters effectively immutable

basics

~20 s

A StorageClass is a named storage tier: a provisioner plus its parameters. When a PersistentVolumeClaim names that class, the provisioner creates real storage and a matching PersistentVolume, which is bound to the claim. No admin pre-creates the volume.

solid answer

~50 s

A **StorageClass** is a cluster-scoped object describing one *tier* of storage the cluster can create on demand. Its key fields are `provisioner` (which driver does the work, e.g. `ebs.csi.aws.com`), `parameters` (driver-specific knobs such as disk type or IOPS), plus `reclaimPolicy`, `volumeBindingMode` and `allowVolumeExpansion`. The flow: a developer writes a PVC asking for a size and `storageClassName: fast-ssd`. The provisioner for that class notices an unbound PVC pointing at it, calls the storage backend to create a real disk, and creates a PersistentVolume object describing it. The control plane binds PV and PVC one-to-one. When a Pod references the PVC, kubelet attaches and mounts that volume into the container. This is **dynamic provisioning**. The alternative is static provisioning, where an administrator hand-creates PV objects in advance. StorageClasses turn storage into self-service: developers ask for a tier and a size, never for a specific disk.

code

yaml · 23 lines
yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast-ssd
provisioner: ebs.csi.aws.com
parameters:
  type: gp3
  iops: "6000"
  encrypted: "true"
reclaimPolicy: Delete
allowVolumeExpansion: true
volumeBindingMode: WaitForFirstConsumer
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: data
spec:
  storageClassName: fast-ssd
  accessModes: ["ReadWriteOnce"]
  resources:
    requests:
      storage: 20Gi

go deeper

for a junior

Be able to say: class = provisioner + parameters, PVC names a class, PV is created automatically and mounted by the Pod. Show a two-file example.

for a middle

Add the field list — reclaimPolicy, volumeBindingMode, allowVolumeExpansion — and describe the provisioner watch/CreateVolume/bind sequence, plus what Pending means.

for a senior

Talk about class names as a portable contract, immutability of provisioner and parameters, driver-specific parameter validation happening only at provision time, and how you smoke-test a new class.

for a principal

Frame classes as the storage policy surface for the platform: how many tiers to expose, naming that survives a cloud migration, and who owns changing them.

## The problem it solves Without StorageClasses, persistent storage is a manual handshake. An administrator provisions a disk in the infrastructure (a cloud block device, an NFS export, a SAN LUN), writes a PersistentVolume (PV) object describing it, and only then can a developer's PersistentVolumeClaim (PVC) find and bind to it. That is *static provisioning*: it does not scale, because every new application waits on a human. A StorageClass replaces the human with a controller. It is a cluster-scoped API object (no namespace) that names a *class* of storage the cluster knows how to manufacture. Developers stop naming disks and start naming tiers: "give me 20Gi of `fast-ssd`". ## Anatomy of a StorageClass - `provisioner` — the identity of the component that creates volumes for this class. Today this is normally a CSI driver name such as `ebs.csi.aws.com`, `pd.csi.storage.gke.io`, `disk.csi.azure.com` or `rook-ceph.rbd.csi.ceph.com`. The special value `kubernetes.io/no-provisioner` means "nothing provisions for this class" and is used for statically created local volumes. - `parameters` — an opaque string map passed to that driver. The keys are defined by the driver, not by Kubernetes: `type: gp3`, `iops: "6000"`, `fsType: ext4`, `encrypted: "true"`. Kubernetes does not validate them; a typo surfaces later as a provisioning error event on the PVC. - `reclaimPolicy` — what happens to the created PV when its claim is deleted. Defaults to `Delete`; `Retain` keeps the volume and its data. - `volumeBindingMode` — `Immediate` (bind and provision as soon as the PVC exists) or `WaitForFirstConsumer` (wait until a Pod using it is scheduled, so the volume lands in the right topology). - `allowVolumeExpansion` — whether a bound PVC of this class may later request more space. - `mountOptions` and `allowedTopologies` — mount flags applied to created PVs, and a restriction on which zones/nodes the class may provision into. ## What actually happens on `kubectl apply` of a PVC 1. The PVC is created with `spec.storageClassName: fast-ssd`, an access mode and `resources.requests.storage`. 2. If no existing PV matches, the PVC stays `Pending`. 3. The external provisioner sidecar for the class watches unbound PVCs whose class points at its driver name. It calls `CreateVolume` on the driver. 4. The driver creates the real disk in the backend and returns its identifier and capacity. 5. The provisioner creates a PV object with `spec.storageClassName: fast-ssd`, a `claimRef` pointing back at the PVC, and the driver's volume handle. 6. Binding controller marks both `Bound`. The PVC's `spec.volumeName` now names the PV. 7. When a Pod mounting that PVC is scheduled, the attach/detach controller attaches the device to the node and kubelet formats (if needed) and mounts it into the container path. Capacity semantics matter here: the PVC *request* is a minimum. A backend that only sells 10Gi increments may return more, and the PV records the real capacity. ## Class names are the contract The class name is what application manifests hardcode, so treat it as an API. `standard`, `fast-ssd`, `retain-db` are portable names you can point at different provisioners in different clusters; a name like `gp3-encrypted-us-east-1a` leaks provider detail into every chart. Most fields of an existing StorageClass — notably `provisioner`, `parameters` and `reclaimPolicy` — are effectively immutable: you cannot re-tune a class in place. The normal workflow is to create a new class and migrate workloads to it. Editing an existing PV's reclaim policy is still possible and is a common one-off rescue. ## Where it sits relative to the rest of storage StorageClass is the *policy* layer: it says what kind of volume to make and under which rules. The PV is the *resource*, the PVC is the *request*, and the CSI driver is the *mechanism*. A cluster can have many classes at once, including one flagged as the default so PVCs that name no class still work. A useful mental check for interviews: if you can delete every PV in the cluster and a redeployed application still comes up with fresh storage, you have dynamic provisioning working. If it comes up `Pending` forever, someone was relying on hand-made PVs.

  • What happens if a PVC names a StorageClass that does not exist in the cluster?
    The PVC is accepted by the API server but stays Pending forever, because no provisioner claims it and no pre-created PV carries that class name. `kubectl describe pvc` shows a `ProvisioningFailed` or simply no provisioning events. Any Pod mounting it stays Pending too, with a FailedScheduling event about an unbound claim.
  • Who validates the values in a StorageClass `parameters` map?
    Only the driver, at provisioning time. Kubernetes treats parameters as an opaque string map and will happily accept nonsense keys or values. The failure surfaces asynchronously as a ProvisioningFailed event on the first PVC that uses the class, which is why a new class should always be smoke-tested with a throwaway PVC.

A StorageClass is a menu item, not a plate of food. The PVC is the order ("one fast-ssd, 20Gi"); the provisioner is the kitchen that makes it to order; the PV is the plate that comes back.

saying these in an interview costs you the question

  • Saying a StorageClass is namespaced or must exist in each namespace
  • Thinking the StorageClass itself holds the data or capacity
  • Believing the PVC gets exactly the requested size (it is a minimum)
  • Assuming you can edit provisioner or parameters on an existing class in place
  • Confusing StorageClass with the PV: the class is policy, the PV is the resource

context

open as a page

In a Kubernetes StorageClass, what is the difference between volumeBindingMode: Immediate and volumeBindingMode: WaitForFirstConsumer, and what production failure does the second one prevent?

level: seniorimportance: must knowfreq 52%

basics

~20 s

Immediate provisions and binds a volume as soon as the claim exists, before any Pod is scheduled, so the disk can land in a zone or node the Pod cannot reach. WaitForFirstConsumer delays provisioning until a Pod using the claim is scheduled, letting the scheduler's decision drive volume placement.

open as a page

A PersistentVolumeClaim is running out of space. What does the allowVolumeExpansion field on a Kubernetes StorageClass permit, and what are the exact steps and limits of growing an existing claim?

level: middleimportance: should knowfreq 42%

basics

~20 s

With allowVolumeExpansion: true on the claim's StorageClass, you edit the PVC's spec.resources.requests.storage upward and the CSI driver grows the disk, then the filesystem. Shrinking is never allowed, and the flag only affects claims whose class had it set.

open as a page

One PersistentVolumeClaim omits the storageClassName field entirely; another sets storageClassName to the empty string "". Explain what Kubernetes does in each case, and how a cluster declares which storage tier is the default.

level: middleimportance: should knowfreq 45%

basics

~20 s

Omitting storageClassName lets the DefaultStorageClass admission controller fill in the cluster's default class, marked by the annotation storageclass.kubernetes.io/is-default-class: "true". Setting it to "" opts out of dynamic provisioning entirely, so the claim can only bind to a pre-created PV that also has no class.

open as a page