Skip to main content
RunBook Academy

KubernetesL · PersistentVolumes and ClaimsPersistentVolumes and Claims

PV-PVC anti-patterns — common storage mistakes and how to avoid them

Advanced⏱ ~17 minkubectl

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

Not yet marked complete on this device.

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

  1. Q1. A team deploys a PostgreSQL StatefulSet with emptyDir for the data volume. The Pod is evicted and rescheduled. What is the consequence?

  2. Q2. A database with `reclaimPolicy: Delete` and no tested snapshot restore procedure is a data-loss incident waiting to happen.

  3. 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.

  4. 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.