Docs
/
Docker Kubernetes
Chapter 13
13 — Kubernetes Storage
The Problem
Containers lose data on restart. Kubernetes provides PersistentVolumes for durable storage.
Key Concepts
PersistentVolume (PV) → The actual storage (disk, NFS, cloud volume)
PersistentVolumeClaim (PVC) → A request for storage by a pod
StorageClass → Defines how PVs are dynamically provisioned
Pod → PVC → PV → Physical Storage
PersistentVolume & PersistentVolumeClaim
# PV (admin creates)
apiVersion: v1
kind: PersistentVolume
metadata:
name: pg-pv
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
hostPath:
path: /data/postgres
---
# PVC (developer creates)
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pg-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
---
# Pod uses PVC
apiVersion: apps/v1
kind: Deployment
metadata:
name: postgres
spec:
replicas: 1
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:16-alpine
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
volumes:
- name: data
persistentVolumeClaim:
claimName: pg-pvc
Access Modes
| Mode | Abbreviation | Description |
|---|---|---|
| ReadWriteOnce | RWO | Single node read/write |
| ReadOnlyMany | ROX | Multiple nodes read-only |
| ReadWriteMany | RWX | Multiple nodes read/write |
StorageClass (Dynamic Provisioning)
No need to pre-create PVs — the StorageClass provisions them on demand.
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast-ssd
provisioner: ebs.csi.aws.com # AWS EBS
parameters:
type: gp3
reclaimPolicy: Retain # Keep data after PVC deleted
volumeBindingMode: WaitForFirstConsumer
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: db-pvc
spec:
storageClassName: fast-ssd # Uses StorageClass
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 20Gi
| Reclaim Policy | Behavior |
|---|---|
| Retain | PV kept after PVC deleted (manual cleanup) |
| Delete | PV and storage deleted with PVC |
StatefulSet Volume Claims
Each pod gets its own PVC automatically.
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgres
spec:
replicas: 3
serviceName: postgres
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:16-alpine
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: fast-ssd
resources:
requests:
storage: 10Gi
# Creates: data-postgres-0, data-postgres-1, data-postgres-2
Key Takeaways
- PVC = request for storage; PV = the actual storage; StorageClass = dynamic provisioner
- Use StorageClass for dynamic provisioning (no manual PV creation)
- Use
Retainreclaim policy for databases — prevents accidental data loss - StatefulSet + volumeClaimTemplates gives each pod its own persistent volume
- Use RWO for databases (single writer), RWX for shared file storage