Storage
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.
Volumes
Section titled “Volumes”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: v1kind: PersistentVolumeClaimmetadata: name: data-pvcspec: accessModes: - ReadWriteOnce resources: requests: storage: 10Gi storageClassName: standardUse the PVC in a pod:
spec: containers: - name: db volumeMounts: - name: data mountPath: /var/lib/data volumes: - name: data persistentVolumeClaim: claimName: data-pvcStorageClasses
Section titled “StorageClasses”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.
kubectl get storageclassVolume 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.
Topology-aware StorageClasses
Section titled “Topology-aware StorageClasses”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.
PVC stuck Pending — triage
Section titled “PVC stuck Pending — triage”kubectl describe pvc <name> -n <ns>— read Events at the bottom.- Common causes: no default StorageClass and missing
storageClassName, StorageClass not found, quota exceeded, WaitForFirstConsumer with no schedulable pod, topology mismatch, provisioner outage. - 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:
| Step | Who owns it |
|---|---|
| Provisioning | CSI controller + StorageClass provisioner — creates backing LUNs/disks/filesystems when a PVC should exist. |
| Attach | CSI attacher (controller sidecar) — tells the backend to present the volume to the node where the pod is scheduled. |
| Mount | kubelet invokes the CSI node plugin — format (if volumeMode: Filesystem and empty), then bind-mount into the pod’s mount namespace. |
| Snapshot / resize | Optional 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.
volumeMode: Filesystem vs Block
Section titled “volumeMode: Filesystem vs Block”spec.volumeMode defaults to Filesystem — the volume is formatted (when needed) and mounted as a directory tree.
volumeMode | Use when |
|---|---|
| Filesystem | Typical databases and apps that expect files under a path. |
| Block | Raw 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.
ConfigMaps
Section titled “ConfigMaps”Store non-sensitive configuration as key-value pairs. Inject into pods as environment variables or mounted files.
apiVersion: v1kind: ConfigMapmetadata: name: app-configdata: DATABASE_HOST: "db.example.com" LOG_LEVEL: "info"Use as environment variables:
env: - name: DATABASE_HOST valueFrom: configMapKeyRef: name: app-config key: DATABASE_HOSTOr mount as a file:
volumeMounts: - name: config mountPath: /etc/configvolumes: - name: config configMap: name: app-configSecrets
Section titled “Secrets”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: v1kind: Secretmetadata: name: db-secrettype: Opaquedata: 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: passwordEncryption at rest vs real secret hygiene
Section titled “Encryption at rest vs real secret hygiene”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
getSecrets 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.
Key Takeaways
Section titled “Key Takeaways”- 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.