KubernetesL · PersistentVolumes and ClaimsPersistentVolumes and Claims
PV-PVC anti-patterns — common storage mistakes and how to avoid them
What you'll learn
- Identify the common PV-PVC anti-patterns in production
- Explain why each anti-pattern causes data loss or unavailability
- Apply the production pattern for correct PV-PVC usage
- Audit a cluster for storage anti-patterns
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
Storage anti-patterns in Kubernetes are silent until the incident: a database Pod is rescheduled and the data is gone; a PVC is deleted and the volume is gone; a multi-node mount fails because the access mode does not match the backend. This lesson walks the common anti-patterns and the production discipline for avoiding them.
Anti-pattern 1: emptyDir for stateful data
The most common data-loss anti-pattern:
# WRONG
volumes:
- name: data
emptyDir: {}
emptyDir is ephemeral: when the Pod is deleted, the data
is gone. A database using emptyDir for its data files
appears to work until the Pod is rescheduled, at which
point the data is gone.
Fix: use PVC for any data the application considers
state. emptyDir is for scratch, cache, and process state.
Anti-pattern 2: hostPath for databases
hostPath ties the data to the node:
# WRONG
volumes:
- name: data
hostPath:
path: /var/lib/data
A database using hostPath is tied to one node. A second
node in the cluster cannot mount the same data; HA is
impossible. Node failure destroys the data.
Fix: use PVC with a CSI driver. Even if the cluster does not have a CSI driver, deploy one (Rook-Ceph, Longhorn, OpenEBS for on-prem; EBS, GCE PD for cloud) before migrating stateful workloads.
Anti-pattern 3: Delete reclaim on critical data
A StorageClass with reclaimPolicy: Delete for a database:
# WRONG
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: db-default
provisioner: ebs.csi.aws.com
reclaimPolicy: Delete # <-- wrong for databases
parameters:
type: gp3
Accidental PVC deletion destroys the EBS volume. The database’s data is gone.
Fix: use Retain for databases. Have a tested
snapshot restore procedure as the safety net.
Anti-pattern 4: mismatched access modes
A PVC requesting ReadWriteMany on a backend that does
not support it:
# WRONG (on EBS)
spec:
accessModes: ["ReadWriteMany"]
storageClassName: ebs-ssd
The PVC remains Pending; the workload cannot start. The operator sees a Pending PVC and wonders why.
Fix: align the access mode with the backend’s capability. EBS supports RWO; NFS supports RWX. The StorageClass and the PVC’s access mode must match.
Anti-pattern 5: missing backup strategy
A database with no snapshot schedule, no backup procedure, no tested restore:
# WRONG (no backup, no snapshot, no runbook)
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgres
spec:
template:
spec:
containers:
- name: postgres
image: postgres:16
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
volumeClaimTemplates:
- metadata:
name: data
spec:
storageClassName: db-default
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 100Gi
The database runs; the data is written; no backup exists. A disk failure, an accidental deletion, or a storage backend outage destroys the data with no recovery path.
Fix: schedule snapshots, test the restore procedure, document the recovery in the runbook.
Anti-pattern 6: PVC bound to wrong StorageClass
A PVC that accidentally binds to a standard StorageClass
when the workload requires ssd:
# WRONG
spec:
storageClassName: standard # should be db-ssd
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 100Gi
The PVC binds to a standard PV; the workload runs on slow storage; the database’s performance is poor.
Fix: specify the StorageClass explicitly; do not rely on defaults for production workloads.
Anti-pattern 7: unbounded sizeLimit on emptyDir
# WRONG (no sizeLimit)
volumes:
- name: scratch
emptyDir: {}
A workload that fills the emptyDir can fill the node’s disk, triggering node pressure eviction. All Pods on the node are evicted; the node reports DiskPressure; new Pods cannot schedule.
Fix: set sizeLimit on every emptyDir; monitor the
emptyDir’s consumption.
Anti-pattern 8: subPath with ConfigMap updates
# WRONG (subPath hides ConfigMap updates)
volumeMounts:
- name: config
mountPath: /app/config/app.conf
subPath: app.conf
volumes:
- name: config
configMap:
name: app-config
When the ConfigMap is updated, the kubelet does not update the subPath mount. The application continues to see the old configuration.
Fix: mount the ConfigMap at the parent path (e.g.,
/app/config); the application reads app.conf from
there. Updates are visible.
The audit
A cluster audit for storage anti-patterns:
# Find Pods using emptyDir with large mounts
kubectl get pods -A -o json | \
jq '.items[] | select(.spec.volumes[]? | .emptyDir != null) |
{name: .metadata.name, namespace: .metadata.namespace,
emptyDir: .spec.volumes[] | select(.emptyDir != null) | .emptyDir}'
# Find PVCs without an explicit StorageClass (relying on default)
kubectl get pvc -A -o json | \
jq '.items[] | select(.spec.storageClassName == null) |
{name: .metadata.name, namespace: .metadata.namespace}'
# Find Released PVs
kubectl get pv -o json | \
jq '.items[] | select(.status.phase == "Released") |
{name: .metadata.name, capacity: .spec.capacity.storage,
reclaimPolicy: .spec.persistentVolumeReclaimPolicy}'
The audit output is the action list. Each item is a remediation task: change the emptyDir to PVC, set the StorageClass explicitly, decide on the Released PV.
Quiz
Knowledge check · 4 questions
Q1. A team deploys a PostgreSQL StatefulSet with emptyDir for the data volume. The Pod is evicted and rescheduled. What is the consequence?
Q2. A database with `reclaimPolicy: Delete` and no tested snapshot restore procedure is a data-loss incident waiting to happen.
Q3. Your team has inherited a cluster with several storage anti-patterns. Design the remediation plan.
Audit findings: 5 databases using emptyDir for data; 3 databases on hostPath; 10 PVCs relying on the default StorageClass; 25 Released PVs with Retain reclaim; 1 database with no snapshot schedule.
Q4. Name three storage anti-patterns and explain why each is dangerous.
Passing score: 75%. Answers are checked in this browser.
Production discipline
- Audit storage regularly. emptyDir for stateful data, hostPath for databases, Delete reclaim on critical data, missing backup strategy — each is a silent incident waiting to happen.
- Never use emptyDir for stateful data. Use PVC.
- Never use hostPath for databases. Use PVC with a CSI driver.
- Retain for stateful data; Delete for ephemeral. The reclaim policy is the backup commitment.
- Always have a tested backup strategy. Snapshots that are never restored are not backups.
- Specify StorageClass explicitly. Do not rely on defaults for production workloads.