skip to content

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