Skip to main content
RunBook Academy

KubernetesL · PersistentVolumes and ClaimsPersistentVolumes and Claims

PV-PVC binding — how Kubernetes matches storage requests to volumes

Advanced⏱ ~16 minkubectl

What you'll learn

  • Describe the binding controller's matching algorithm
  • Distinguish dynamic from static binding
  • Identify the edge cases that leave a PVC Pending
  • Apply selectors and volumeName for explicit binding

Prerequisites

Verified against Kubernetes 1.34.x · kubeadm 1.34.x · kubectl 1.34.x · etcd 3.6.x · CoreDNS 1.11.x · containerd 1.7.x / 2.x · 2026-08-16

Not yet marked complete on this device.

PV-PVC binding is the controller in the control plane that matches storage requests to actual volumes. This lesson walks the matching algorithm, the edge cases that leave PVCs Pending, and the production patterns for explicit binding.

The binding algorithm

The binding controller runs in the kube-controller-manager. For each unbound PVC, it:

  1. Looks for an existing PV that matches the PVC’s criteria.
  2. If no PV matches and the PVC has a StorageClass, calls the provisioner to create a PV.
  3. If no PV is created (no StorageClass, provisioner fails), the PVC remains Pending.

The matching criteria:

  • Capacity: PV’s capacity >= PVC’s request.
  • Access modes: PV must support at least one of PVC’s requested modes.
  • StorageClass: PV’s storageClassName == PVC’s storageClassName (or both are unset).
  • Selector: if PVC has a selector, PV’s labels must match.
  • volumeName: if PVC has a volumeName, that exact PV must match.

The first PV that satisfies all criteria is bound.

Dynamic vs static binding

Dynamic binding

sequenceDiagram
    participant U as User
    participant API as API server
    participant SC as StorageClass
    participant CSI as CSI provisioner
    participant N as Storage backend
    U->>API: submit PVC with storageClassName: ssd
    API->>SC: look up provisioner
    SC->>CSI: provision volume
    CSI->>N: create backend storage
    N->>CSI: volumeHandle
    CSI->>API: create PV
    API->>U: PVC bound to new PV

The user submits a PVC with a StorageClass; the provisioner creates the PV; the binding is automatic.

Static binding

For pre-existing volumes, the operator creates the PV manually:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: manual-pv
  labels:
    workload: legacy
spec:
  capacity:
    storage: 500Gi
  accessModes:
    - ReadWriteMany
  storageClassName: legacy
  nfs:
    server: nfs.prod.example.com
    path: /exports/legacy

The user submits a PVC:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: legacy-data
spec:
  accessModes: ["ReadWriteMany"]
  storageClassName: legacy
  selector:
    matchLabels:
      workload: legacy
  resources:
    requests:
      storage: 100Gi

The PVC’s selector matches the PV’s labels; the binding is static.

The selector field

The PVC’s selector matches PVs by labels:

selector:
  matchLabels:
    workload: legacy
  matchExpressions:
  - key: environment
    operator: In
    values: ["production"]

A PVC with a selector does not match PVs whose labels do not match. This is useful for:

  • Pre-allocating PVs for specific workloads.
  • Restricting which PVs a PVC can bind to.
  • Avoiding accidental binding to unrelated PVs.

The volumeName field

The PVC’s volumeName binds to a specific PV by name:

spec:
  volumeName: my-pv

The binding controller looks up the PV by name and binds if it matches. If the PV does not exist or does not match, the PVC is Pending.

volumeName is used for:

  • Recovering a released PV (binding to a specific PV).
  • Pre-allocating a PV for a specific workload.
  • Reserving a PV before it is needed.

Edge cases that leave PVCs Pending

A PVC remains Pending when:

  • No matching PV exists and the StorageClass has no provisioner (or the provisioner is down).
  • The StorageClass does not match: the PVC asks for ssd, only standard PVs are available.
  • The access mode is not supported: the PVC asks for RWX, only RWO PVs are available.
  • The capacity is insufficient: all available PVs are smaller than the request.
  • The selector matches no PV: the PVC has a selector that no available PV matches.
  • The volumeName points to a non-existent or non-matching PV: explicit binding fails.
# Substitute your own values before running:
PVC=data-postgres-0
NS=production

# Diagnose a Pending PVC
kubectl describe pvc "$PVC" -n "$NS"
# Events show the reason:
# "no persistent volumes available for this claim and no storage class is set"
# "persistentvolumeclaim requires matching volumeMode, accessMode, and storageClassName"

Quiz

Knowledge check · 4 questions

  1. Q1. A PVC is submitted with `storageClassName: ssd`. The cluster has only `standard` StorageClass PVs. What happens?

  2. Q2. A PVC with a selector that matches no available PV remains Pending until a matching PV is created.

  3. Q3. A team submits a PVC requesting 100 GB on the `ssd` StorageClass. The PVC is Pending. Walk through the diagnostic.

    PVC: storageClassName: ssd, 100 GB, ReadWriteOnce. Status: Pending. Events show "waiting for a volume to be created" or "no persistent volumes available".

  4. Q4. Explain the difference between a PVC selector and a PVC volumeName, and when each is appropriate.

Passing score: 75%. Answers are checked in this browser.

Production discipline

  • The StorageClass must match. A PVC on ssd does not bind to a standard PV.
  • The selector is for static binding. It does not trigger provisioning.
  • The volumeName is for explicit binding. Use it for recovery and reservation.
  • Pending PVCs are diagnostic events. Always check kubectl describe pvc for the reason.
  • The provisioner is the bottleneck. A slow provisioner leaves PVCs Pending. Monitor provisioner latency.