Skip to content

Storage

First PublishedLast UpdatedByAtif Alam

Containers are ephemeral by default — when a pod restarts, any data written to the container’s filesystem is lost. Kubernetes provides several ways to persist or share data.

A Volume is a directory accessible to containers in a pod. Its lifecycle is tied to the pod (not the container). Common types:

  • emptyDir — Created when a pod starts, deleted when the pod is removed. Useful for scratch space or sharing files between containers in the same pod.
  • hostPath — Mounts a file or directory from the host node’s filesystem. Use with caution (ties pod to a specific node).
spec:
containers:
- name: app
volumeMounts:
- name: scratch
mountPath: /tmp/data
volumes:
- name: scratch
emptyDir: {}

PersistentVolumes and PersistentVolumeClaims

Section titled “PersistentVolumes and PersistentVolumeClaims”

For data that outlives a pod (databases, file uploads), use PersistentVolumes (PV) and PersistentVolumeClaims (PVC).

  • PV — A piece of storage provisioned by an admin or dynamically by a StorageClass.
  • PVC — A request for storage by a user. Kubernetes binds the PVC to a matching PV.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: data-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
storageClassName: standard

Use the PVC in a pod:

spec:
containers:
- name: db
volumeMounts:
- name: data
mountPath: /var/lib/data
volumes:
- name: data
persistentVolumeClaim:
claimName: data-pvc

A StorageClass defines how storage is provisioned (e.g. SSD vs HDD, which cloud provider backend). When a PVC references a StorageClass, Kubernetes automatically provisions a PV.

Terminal window
kubectl get storageclass

Volume binding: Immediate vs WaitForFirstConsumer

Section titled “Volume binding: Immediate vs WaitForFirstConsumer”
  • volumeBindingMode: Immediate — the provisioner may bind and provision before a pod exists. Fast, but can pin a volume to a zone that later conflicts with scheduling constraints.
  • volumeBindingMode: WaitForFirstConsumer — provisioning waits until a pod is scheduled; the volume lands in a topology compatible with that pod. Prefer this for multi-zone clusters and zone-scoped storage.

When nodes span availability zones, ensure allowedTopologies (or CSI driver defaults) line up with where your workloads run. Misaligned topology is a common reason for Pending pods even when “storage exists.”

Pod delete vs reschedule — what happens to PVC data?

Section titled “Pod delete vs reschedule — what happens to PVC data?”
  • Deleting a Pod does not delete its PVC by default — data usually remains for StatefulSets and standard Deployments using PVCs.
  • Rescheduling to another node requires the volume’s access mode and CSI driver to support attach/detach in the target zone (ReadWriteOnce is single-writer — cannot mount twice).
  • For StatefulSets, the same claim name follows the stable identity when the replacement pod can attach in the same topology.
  1. kubectl describe pvc <name> -n <ns> — read Events at the bottom.
  2. Common causes: no default StorageClass and missing storageClassName, StorageClass not found, quota exceeded, WaitForFirstConsumer with no schedulable pod, topology mismatch, provisioner outage.
  3. Cross-check namespace ResourceQuota and LimitRange if events mention quota.

For EKS-oriented storage triage, see the EKS troubleshooting cheat sheet.

CSI drivers and the attach–detach–mount path

Section titled “CSI drivers and the attach–detach–mount path”

Container Storage Interface (CSI) splits responsibilities between control-plane controllers and per-node plugins:

StepWho owns it
ProvisioningCSI controller + StorageClass provisioner — creates backing LUNs/disks/filesystems when a PVC should exist.
AttachCSI attacher (controller sidecar) — tells the backend to present the volume to the node where the pod is scheduled.
Mountkubelet invokes the CSI node plugin — format (if volumeMode: Filesystem and empty), then bind-mount into the pod’s mount namespace.
Snapshot / resizeOptional capabilities exposed as CRDs (for example VolumeSnapshot) when the driver supports them.

The kubelet does not phone the storage array directly; it drives CSI NodePublishVolume RPCs. That is why topology and WaitForFirstConsumer matter: scheduling and volume placement must line up.

spec.volumeMode defaults to Filesystem — the volume is formatted (when needed) and mounted as a directory tree.

volumeModeUse when
FilesystemTypical databases and apps that expect files under a path.
BlockRaw device without a cluster-managed filesystem — some databases and high-throughput workloads manage layout themselves; consumers use volumeDevices instead of volumeMounts.

Not every CSI driver supports Block equally; confirm access modes and attach limits in the driver’s matrix before relying on it in production.

Store non-sensitive configuration as key-value pairs. Inject into pods as environment variables or mounted files.

apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
DATABASE_HOST: "db.example.com"
LOG_LEVEL: "info"

Use as environment variables:

env:
- name: DATABASE_HOST
valueFrom:
configMapKeyRef:
name: app-config
key: DATABASE_HOST

Or mount as a file:

volumeMounts:
- name: config
mountPath: /etc/config
volumes:
- name: config
configMap:
name: app-config

Like ConfigMaps but for sensitive data (passwords, tokens, TLS certs). Values are base64-encoded (not encrypted by default — enable encryption at rest for production).

apiVersion: v1
kind: Secret
metadata:
name: db-secret
type: Opaque
data:
password: cGFzc3dvcmQxMjM= # base64 of "password123"

Use as environment variables or volume mounts, same pattern as ConfigMaps:

env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-secret
key: password

Encryption at rest for etcd (often backed by a cloud KMS) protects etcd data on disk and in backups. It is still not a substitute for:

  • RBAC and audit on who can get Secrets via the API.
  • Rotation, short-lived credentials, and safe CI/CD handling of manifests.
  • Runtime secret delivery when you need centralized rotation — see Helm templating — External Secrets and Vault for Vault Agent and External Secrets Operator patterns.
  • Use emptyDir for scratch space; PVC for persistent data.
  • StorageClasses enable dynamic provisioning — no need to pre-create PVs; CSI separates provision/attach from kubelet mount.
  • volumeMode chooses Filesystem (default mount) vs Block (raw device) when the driver supports it.
  • ConfigMaps for config, Secrets for sensitive data. Both inject into pods as env vars or files.
  • Secrets are base64-encoded in the API; enable encryption at rest and still treat RBAC + rotation + external stores as the full story.