Part of the series Kubernetes storage

Kubernetes storage explained: StorageClass, PV, and PVC

, 6 min read

An application needs somewhere to keep its data. In Kubernetes, that involves several objects that describe different parts of the request. The application asks for storage through a PVC; Kubernetes connects that claim to a PV. A StorageClass describes the kind of storage and how it should be provisioned.

The diagrams below explain those relationships and then follow dynamic provisioning in order. Use Play to watch the phases, or Next to study one phase at a time. Each diagram also works as a complete static reference with JavaScript disabled or reduced motion enabled.

StorageClass, PV, PVC, and Pod in plain English

Object What it means Scope
StorageClass (SC) A storage policy: which provisioner to use and which provisioning settings apply. Cluster-wide
PersistentVolume (PV) A Kubernetes object representing storage supplied by a storage system or administrator. The PV object is distinct from the physical disk or network share. Cluster-wide
PersistentVolumeClaim (PVC) An application’s request for storage, including capacity and access requirements. Namespace
Pod The workload that references the PVC and exposes the volume to a container. Namespace

For example, a claim might request 10Gi from a class named fast-ssd. The Pod references that claim by name and mounts the volume at /data.

The Pod’s volumes entry identifies the claim. Its container’s volumeMounts entry selects where the files appear inside the container. For background on the workload itself, see What is a Kubernetes Pod?.

Dynamic provisioning: storage created on demand

With dynamic provisioning, a supported provisioner creates storage to satisfy a claim when the class’s binding policy allows it. The administrator does not need to create each PV manually. This relationship is described in the official dynamic provisioning documentation.

Open the dynamic provisioning diagram in a full browser tab.

Read the diagram as relationships: Pod → PVC → StorageClass → provisioner. The provisioner requests the actual storage and creates a PV representing it. Kubernetes then binds the claim and PV.

A StorageClass is a policy, not a storage device. Creating the class alone does not create a disk. The provisioner and its storage backend must already be configured.

The architecture view explains who refers to whom. It is not the order of every API operation, and application file reads and writes do not pass through the StorageClass or provisioning sidecar.

WaitForFirstConsumer: why the PVC can stay Pending

Some storage is tied to a node or availability zone. Provisioning before the workload’s placement is understood can produce a volume that the Pod cannot use.

volumeBindingMode: WaitForFirstConsumer delays binding and provisioning until a consuming Pod lets Kubernetes consider scheduling constraints. See the StorageClass volume binding documentation.

Open the provisioning sequence in a full browser tab.

The sequence has six phases:

  1. Create a PVC. The request can remain Pending while it waits for a consumer.
  2. Create a Pod that uses the claim. The scheduler observes the workload.
  3. Select a candidate node. Scheduling takes storage topology into account. In this example, the selected node is recorded on the claim.
  4. Provision storage and create a PV. The external-provisioner coordinates with the CSI driver to create suitable backing storage and its PV representation.
  5. Bind the PVC and PV. The claim and volume become associated.
  6. Complete Pod binding and prepare the volume. Attachment, where required, and node-side mounting make the volume available to the container.

Choosing a candidate node is different from completing Pod scheduling. The diagram separates these events so the delayed-binding process is easier to follow.

A claim waiting for its first consumer is not necessarily broken. Check its Events before treating Pending as a provisioning failure.

Static provisioning: the PV exists first

An administrator can also prepare storage and create PVs ahead of time. Kubernetes matches a suitable available PV to a claim instead of asking a provisioner to create a new one.

Watch the static provisioning flow

Open the static provisioning diagram in a full browser tab.

The class in this example uses kubernetes.io/no-provisioner. That class creates neither disks nor PVs. If no suitable PV exists, the claim has nothing to bind to.

For local volumes, the administrator also describes the PV’s node affinity so Kubernetes knows which node can access it. Matching involves more than the class name: capacity, access requirements, and placement must also be suitable. See the PersistentVolume documentation.

Example: a StorageClass, PVC, and consuming Pod

This example follows the AWS EBS CSI driver named in the supplied architecture diagram. It requires that driver, its controller and node components, suitable permissions, and a compatible cluster. It is an illustrative configuration, not a deployment tested for this article.

The StorageClass selects the EBS CSI driver and uses delayed binding:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast-ssd
provisioner: ebs.csi.aws.com
parameters:
  type: gp3
volumeBindingMode: WaitForFirstConsumer
reclaimPolicy: Retain

The claim requests capacity from that class:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: my-data
spec:
  storageClassName: fast-ssd
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi

The Pod references the claim and mounts it at /data:

apiVersion: v1
kind: Pod
metadata:
  name: storage-demo
spec:
  containers:
    - name: app
      image: busybox:1.37
      command: ["sh", "-c", "sleep 3600"]
      volumeMounts:
        - name: data
          mountPath: /data
  volumes:
    - name: data
      persistentVolumeClaim:
        claimName: my-data

Create the PVC and Pod in the same namespace. This example uses ReadWriteOnce, which describes read/write mounting by one node; it does not by itself guarantee that only one Pod can use the volume.

Retain is explicit here so deleting a claim does not automatically reclaim its backing storage. It requires an administrator to manage eventual storage cleanup. Reclaim policy is separate from a backup strategy; the PV lifecycle documentation explains the distinction between retention and deletion.

Troubleshoot the request before the mount

Start with the class, claim, and Pod. Add -n YOUR_NAMESPACE where needed:

kubectl get storageclass
kubectl get pvc,pods
kubectl describe pvc my-data
kubectl describe pod storage-demo
kubectl get pv
Symptom What to inspect
PVC is Pending, with no consuming Pod Check whether its class uses WaitForFirstConsumer.
PVC stays Pending after the Pod exists Read claim and Pod Events; check class, scheduling constraints, driver availability, and capacity.
A no-provisioner claim never binds Look for a compatible administrator-created PV.
PVC is Bound, but the Pod cannot start Inspect scheduling, topology, volume attachment, and mount errors in Pod Events and driver logs.
Storage exists but the application cannot write Check filesystem permissions, mount settings, and the application’s user identity.

Bound means the claim has a volume. It does not prove that mounting succeeded or that the application is healthy. Follow the evidence from claim to Pod to node-side storage components.

Download the source diagrams

The interactive views are adapted from the two supplied HTML diagrams. The architecture file is separated into dynamic and static views; the sequence retains its twelve messages, grouped into six playback phases. The controller, API server, storage backend, and node operations are simplified for explanation; real systems also handle retries, failures, and driver-specific behavior.

Use the architecture diagram to understand the references. Use the sequence to understand when provisioning, binding, and mounting happen.