Skip to content

Storage Overview

Container filesystems are ephemeral. For durable data, Kubernetes provides persistent storage primitives.

Ephemeral vs Persistent Storage

Ephemeral options:

  • emptyDir: temporary storage tied to pod lifetime.
  • hostPath: host-node path mount (use sparingly, mainly for node-level agents).

Persistent options:

  • PersistentVolume (PV)
  • PersistentVolumeClaim (PVC)
  • StorageClass

PV, PVC, and StorageClass

Object Purpose Typical owner
PV Actual storage resource Platform automation or admin
PVC Workload storage request App team
StorageClass Provisioning policy Platform team

The indirection exists for the same reason Services sit in front of pods: decoupling. The app team asks for "20Gi, single-writer" without knowing whether it comes from EBS, Ceph, or NFS. The platform team can change the backend without touching a single workload manifest.

How a volume reaches your pod

Dynamic provisioning is a chain of controllers, and each link is a distinct failure point:

sequenceDiagram
    participant U as User
    participant PVC as PVC (Pending)
    participant CSI as CSI provisioner
    participant S as Scheduler
    participant K as kubelet

    U->>PVC: create PVC (class: gp3-encrypted)
    Note over PVC,S: WaitForFirstConsumer: nothing happens<br/>until a pod uses the PVC
    U->>S: create pod referencing PVC
    S->>S: pick node (zone-aware)
    S->>CSI: trigger provisioning for that topology
    CSI->>CSI: create backing volume, create PV
    CSI->>PVC: bind PVC ↔ PV (1:1, exclusive)
    K->>K: attach volume to node, mount into pod

A PVC and PV bind one-to-one and exclusively -- once bound, the pairing holds until the PVC is deleted. When a PVC is deleted, the PV moves to Released and its reclaim policy decides what happens: Delete removes the backing volume; Retain keeps the PV (and data) but it will not rebind automatically -- an admin must clean it and remove the old claimRef first. A Released PV that "won't bind" to a new PVC is expected behavior, not a bug.

PVC Example

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: db-data
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 20Gi
  storageClassName: standard

Access Modes

  • ReadWriteOnce (RWO): mounted read-write by one node at a time. Multiple pods on the same node can all mount it.
  • ReadWriteOncePod (RWOP): mounted read-write by exactly one pod. Stricter than RWO. Added in Kubernetes 1.22 for single-writer guarantees at the pod level.
  • ReadWriteMany (RWX): mounted read-write by many nodes simultaneously. Requires a compatible backend (NFS, CephFS, cloud file shares).
  • ReadOnlyMany (ROX): mounted read-only by many nodes.

StorageClass Example (CSI)

Prefer CSI drivers and dynamic provisioning.

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: gp3-encrypted
provisioner: ebs.csi.aws.com
parameters:
  type: gp3
  encrypted: "true"
allowVolumeExpansion: true
reclaimPolicy: Delete
volumeBindingMode: WaitForFirstConsumer

Note: provisioner is environment-specific. Use the CSI driver for your platform.

volumeBindingMode: WaitForFirstConsumer delays PV provisioning until a pod using the PVC is scheduled. This ensures the volume is created in the same availability zone as the pod. Use this mode for zonal storage backends (most cloud block storage). The default Immediate mode provisions the volume when the PVC is created, which can cause zone mismatches.

Reclaim Policy

  • Delete: deleting PVC removes backing storage.
  • Retain: backing volume remains for manual recovery.

For critical stateful workloads, Retain can reduce accidental data loss risk.

Mounting a PVC in a Pod

apiVersion: v1
kind: Pod
metadata:
  name: app
spec:
  volumes:
    - name: app-storage
      persistentVolumeClaim:
        claimName: db-data
  containers:
    - name: app
      image: nginx:1.27
      volumeMounts:
        - name: app-storage
          mountPath: /data

Volume Snapshots

CSI-backed storage supports point-in-time volume snapshots via the VolumeSnapshot API (requires the external-snapshotter controller):

apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
  name: db-snapshot-2026-05-03
spec:
  volumeSnapshotClassName: csi-snapclass
  source:
    persistentVolumeClaimName: db-data

Restore by creating a new PVC with dataSource pointing to the snapshot. Snapshots are much faster than full backup/restore for large volumes and are useful for pre-upgrade checkpoints.

Operational Checks

kubectl get pvc
kubectl get pv
kubectl describe pvc db-data
kubectl get volumesnapshot

If PVC remains Pending, validate storage class name, CSI driver health, and topology constraints (WaitForFirstConsumer PVCs stay Pending until a pod is scheduled).

Certification notes

  • CKA storage tasks are usually: create a StorageClass/PV/PVC chain, mount it in a pod, and diagnose a Pending PVC. Know that Pending + WaitForFirstConsumer is normal until a pod schedules.
  • Access modes are enforced at attach time, per node -- RWO does not mean "one pod."
  • Remember reclaim policy behavior for Retain vs Delete; the released-PV-won't-rebind case appears in scenarios.