Kubernetes
Advanced 17 min read Kubernetes

Volumes, PVCs & Persistent Storage

Containers are ephemeral, so Kubernetes separates storage from pods. Learn emptyDir and hostPath volumes, PersistentVolumes, PersistentVolumeClaims, StorageClasses and dynamic provisioning, plus access modes and reclaim policies.

emptyDirhostPathPersistentVolumePersistentVolumeClaimStorageClassaccess modesreclaim policydynamic provisioning
Prerequisites: Pods & Containers
Introduction

What is this?

Kubernetes separates storage lifecycle from pod lifecycle. Volumes like emptyDir and hostPath provide pod-scoped or node-scoped storage, while PersistentVolumes (PVs) and PersistentVolumeClaims (PVCs) provide cluster-level storage that survives pod recreation. A StorageClass enables dynamic provisioning: a claim triggers automatic creation of a matching PV from a provisioner such as a cloud disk or CSI driver.

Motivation

Why do you need it?

  • Databases and application state must survive pod and node churn.
  • PVs and PVCs decouple the storage request (how much, what speed) from the storage implementation (disk type, cloud provider).
  • Dynamic provisioning removes the need to pre-create volumes by hand.
  • Access modes and reclaim policies make capacity and cleanup predictable.
Think about it

Mental model

Imagine

Reserving a warehouse unit at a storage company

Think of

A PersistentVolumeClaim

You ask for 1GiB of racked space (the PVC). The company matches you to an actual unit (the PV) with the right features. When you leave, the company either wipes and re-lets it (Delete) or holds it for you (Retain).

Mechanics

How it works

  • A pod declares an emptyDir (pod-lifetime scratch), a hostPath (node path), or a PVC in its volumes.
  • A PVC requests capacity, access mode and a StorageClass; the controller finds or provisions a matching PV.
  • The PV is bound to the PVC (status Bound) and can then be mounted by pods in the claim's namespace.
  • The kubelet mounts the PV at the container path declared in volumeMounts.
  • When the pod is deleted the volume stays; when the PVC is deleted the reclaim policy decides what happens to the PV.

From StorageClass through PVC and PV to a mounted pod.

StorageClass
PVC
PV
Pod volume
Persistent data
Reference

Key commands

kubectl get storageclass

List StorageClasses with provisioner and reclaim policy.

NAME                 PROVISIONER                RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
standard (default)   k8s.io/minikube-hostpath   Delete          Immediate           false                  7m
kubectl get pvc

List claims with their status (Pending or Bound).

NAME         STATUS   VOLUME      CAPACITY   ACCESS MODES   STORAGECLASS   AGE
data-claim   Bound    pvc-3f2a1c   1Gi        RWO            standard       3m
kubectl get pv

List cluster-level PersistentVolumes and their bound claims.

kubectl describe pvc data-claim

Show claim details including capacity, access modes and events.

kubectl get pv pvc-3f2a1c -o yaml

Inspect the PV spec, reclaim policy and status.

kubectl delete pvc data-claim

Release the claim; the reclaim policy then decides the PV fate.

Hands-on

Examples

A PVC bound and mounted by a database pod

A PVC bound and mounted by a database pod
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: data-claim
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
storageClassName: standard
---
apiVersion: v1
kind: Pod
metadata:
name: db
spec:
containers:
- name: db
image: postgres:16
env:
- name: POSTGRES_PASSWORD
value: secret
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
volumes:
- name: data
persistentVolumeClaim:
claimName: data-claim

The claim asks for 1GiB with ReadWriteOnce access on the standard StorageClass, which provisions a PV dynamically. The db pod mounts the claim at PostgreSQL's data directory, so the database survives pod and node failures.

Real world

In production

  • Stateful databases (PostgreSQL, MySQL, Redis) persist data in PVCs so they survive pod rescheduling.
  • StorageClasses map to cloud disk tiers: SSD for databases, HDD for logs.
  • emptyDir is ideal for scratch space and caching that must die with the pod.
  • hostPath is used by daemonsets such as log collectors that need node-local files.
Guidelines

Best practices

  • Always use PVCs and StorageClasses for durable data; reserve emptyDir for ephemeral scratch.
  • Match access modes to reality: RWO for single-node writers, RWX only when multiple nodes truly write.
  • Choose reclaimPolicy Delete for disposable volumes and Retain for anything that must be inspected or recovered.
  • Set storageClassName explicitly and use volumeBindingMode WaitForFirstConsumer for local or zone-aware storage.
Watch out

Common mistakes

  • Putting databases on emptyDir and losing data on every pod recreation.
  • Using hostPath for multi-node workloads, which breaks when the pod moves nodes.
  • Requesting an access mode the underlying storage cannot provide, leaving the PVC Pending.
  • Forgetting that deleting a PVC with reclaimPolicy Delete destroys the volume and its data.
Root cause analysis

Common issues

Error
PVC stuck in Pending
Root cause

No StorageClass provisioner can satisfy the request, or no matching static PV exists.

Fix

Check kubectl get storageclass, set the right storageClassName, and confirm the request fits an available PV.

Error
FailedMount: persistentvolumeclaim not found
Root cause

The pod references a claim that does not exist in its namespace.

Fix

Create the PVC in the same namespace or fix the claimName, then recreate the pod.

Error
PV stuck in Released and the claim will not rebind
Root cause

reclaimPolicy Retain leaves the PV with old data and a claimRef.

Fix

Delete the PV manually (with the data preserved) or use reclaimPolicy Delete for disposable volumes.

When things break

Debugging tips

  • kubectl describe pvc <name> shows why a claim is stuck Pending (no matching storage, wrong class).
  • kubectl get pv -o yaml reveals reclaim policy, claimRef and status (Available/Bound/Released).
  • kubectl get events --sort-by=.lastTimestamp surfaces FailedMount and provisioner errors.
  • kubectl exec into the pod and df -h /mount-path to verify the volume actually mounted.
Interviews

Interview checkpoint

Practice

Hands-on lab

Persistent Storage with PVCs
40 min ยท advanced
In progress
Objectives
  • Inspect StorageClasses and dynamic provisioning
  • Create a PVC and verify it binds to a PV
  • Run a database on persistent storage
  • Prove data survives pod recreation and observe reclaim behavior

Persistent Storage with PVCs

40 min ยท advanced

0/6 steps
0%
sandbox ยท Linux sandbox
$
Try these commands
Test yourself

Quick quiz

0/3
1

Which access mode allows multiple nodes to read and write the same volume?

2

A PVC must be in the same namespace as the Pod that uses it.

3

Which reclaim policy immediately deletes the PersistentVolume when its claim is released?

Quick reference

Cheat sheet

Storage
kubectl get storageclassList storage classes
kubectl get pvcList claims and status
kubectl get pvList persistent volumes
kubectl describe pvc NAMEClaim details and events
kubectl get pv NAME -o yamlPV spec and reclaim policy
kubectl delete pvc NAMERelease the claim
Go deeper

Official documentation