8.2 Volume Types, Snapshots & Stateful Storage Operations
Key Takeaways
- emptyDir lives and dies with the Pod and is shared between its containers, while hostPath mounts a node directory and is a serious security risk in multi-tenant clusters.
- A projected volume assembles ConfigMaps, Secrets, ServiceAccount tokens, and downward API fields into a single mount point.
- volumeBindingMode WaitForFirstConsumer delays PersistentVolume creation until a Pod is scheduled, so zonal storage is provisioned in the zone where the Pod actually lands.
- Volume expansion is supported when the StorageClass sets allowVolumeExpansion true and the CSI driver implements it; shrinking a volume is never supported.
- StatefulSet volumeClaimTemplates create one PersistentVolumeClaim per ordinal, and those claims deliberately survive scale-down and StatefulSet deletion so data is not lost.
8.2 Volume Types, Snapshots & Stateful Storage Operations
Quick Answer: Not every volume is a PersistentVolumeClaim.
emptyDiris scratch space that dies with the Pod;hostPathmounts a node directory and is a privilege-escalation hazard;projectedmerges ConfigMaps, Secrets, tokens, and downward-API fields into one mount; generic ephemeral volumes give you a full CSI volume with Pod lifetime. On top of PVCs sit the day-two operations:WaitForFirstConsumerbinding, volume expansion, and VolumeSnapshots.
Section 8.1 covered the PV/PVC/StorageClass/CSI model. This section covers everything a real stateful workload needs on top of it.
1. Volume Types by Lifetime
| Type | Lifetime | Typical use | Caution |
|---|---|---|---|
emptyDir | Pod | Scratch space, caches, sharing files between containers in a Pod | Deleted when the Pod is removed or rescheduled — not just on container restart |
emptyDir with medium: Memory | Pod | RAM-backed tmpfs for very fast temporary data | Counts against the container's memory limit; a large write can trigger an OOM kill |
hostPath | Node | Node agents that genuinely need host access (log shippers, monitoring) | Major security risk — mounting /var/run/docker.sock or /etc is a documented cluster-takeover path. Restricted by Pod Security Standards. |
configMap / secret | Pod | Injecting configuration and credentials as files | ~1 MiB limit; Secrets are held in tmpfs |
projected | Pod | One mount combining several sources | See below |
downwardAPI | Pod | Exposing Pod name, namespace, labels, and resource limits as files | Read-only |
| Generic ephemeral volume | Pod | A real CSI volume that should be created and destroyed with the Pod | Uses a StorageClass, unlike emptyDir |
persistentVolumeClaim | Independent of the Pod | Databases, queues, uploads — anything that must survive | Covered in 8.1 |
Projected Volumes
A projected volume assembles multiple sources under one path — the mechanism behind the modern ServiceAccount token:
volumes:
- name: app-config
projected:
sources:
- configMap: { name: web-config }
- secret: { name: tls-cert }
- serviceAccountToken:
path: token
expirationSeconds: 3600
audience: vault
- downwardAPI:
items:
- path: pod-name
fieldRef: { fieldPath: metadata.name }
2. Volume Binding Mode
A StorageClass field with real operational consequences:
| Mode | Behaviour |
|---|---|
Immediate | The PersistentVolume is provisioned as soon as the PVC is created |
WaitForFirstConsumer | Provisioning is delayed until a Pod using the PVC is scheduled |
The reason WaitForFirstConsumer is the recommended default for zonal block storage: an AWS EBS volume or GCP persistent disk exists in one availability zone and can only be attached to a node in that zone. With Immediate, the volume might be created in us-east-1a while the scheduler — considering CPU, affinity, and taints — wants to place the Pod in us-east-1c. The Pod then sticks in Pending with a volume-node-affinity conflict. Delaying provisioning lets the scheduler decide first, and the volume is then created where the Pod actually is.
3. Volume Expansion
Growing a volume is a two-condition operation:
- The StorageClass must set
allowVolumeExpansion: true. - The CSI driver must implement the expansion capability.
Then you simply edit the PVC's requested storage upward. Depending on the driver the filesystem is resized online, or on the next Pod restart.
Shrinking is never supported. No CSI driver implements it and Kubernetes rejects a reduced request. Migrating to a smaller volume means creating a new PVC and copying the data.
4. VolumeSnapshots
The CSI snapshot API mirrors the PV/PVC pattern with three objects:
| Object | Role | Analogy |
|---|---|---|
VolumeSnapshotClass | Which driver takes the snapshot and with what parameters | StorageClass |
VolumeSnapshot | A namespaced request for a point-in-time copy of a PVC | PersistentVolumeClaim |
VolumeSnapshotContent | The cluster-scoped object representing the actual snapshot | PersistentVolume |
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
name: orders-db-2026-08-07
namespace: production
spec:
volumeSnapshotClassName: csi-ebs-snapclass
source:
persistentVolumeClaimName: orders-db-pvc
---
apiVersion: v1
kind: PersistentVolumeClaim # restore: a new PVC sourced from the snapshot
metadata:
name: orders-db-restored
spec:
accessModes: [ReadWriteOnce]
storageClassName: fast-ebs
dataSource:
name: orders-db-2026-08-07
kind: VolumeSnapshot
apiGroup: snapshot.storage.k8s.io
resources:
requests: { storage: 50Gi }
CSI also supports cloning — creating a PVC whose dataSource is another PVC directly.
A snapshot is crash-consistent, not application-consistent. It captures the block device as it is at that instant, exactly like pulling the power cord. A database may need to replay its write-ahead log on restore. Application-consistent backups require quiescing the application first, which is precisely one of the jobs a database Operator performs.
5. StatefulSet Storage Behaviour
volumeClaimTemplates creates one PVC per Pod ordinal, named <template>-<statefulset>-<ordinal>:
StatefulSet: orders-db (replicas: 3)
orders-db-0 ──► PVC data-orders-db-0 ──► PV (50Gi, zone a)
orders-db-1 ──► PVC data-orders-db-1 ──► PV (50Gi, zone b)
orders-db-2 ──► PVC data-orders-db-2 ──► PV (50Gi, zone c)
Three behaviours that surprise people, all of them deliberate:
- Scaling down does not delete the PVC. Scaling from 3 to 1 removes Pods
orders-db-2andorders-db-1but leaves their PVCs. Scaling back up reattaches the same data. That is a data-safety feature, not a bug — but it does mean you keep paying for the storage. - Deleting the StatefulSet does not delete the PVCs either, unless a
persistentVolumeClaimRetentionPolicysays otherwise. volumeClaimTemplatesis immutable on an existing StatefulSet, so changing the requested size there is rejected. Expanding StatefulSet storage means editing each PVC individually (with a StorageClass allowing expansion).
The optional retention policy makes cleanup explicit:
spec:
persistentVolumeClaimRetentionPolicy:
whenDeleted: Delete # or Retain (default)
whenScaled: Retain # or Delete
6. Access Modes Constrain Topology, Not Concurrency
A recurring misreading: ReadWriteOnce means one node, not one Pod. Several Pods scheduled onto the same node can all mount the same RWO volume read-write. If you need a hard single-writer guarantee across the whole cluster, that is ReadWriteOncePod (RWOP). And a volume can only be mounted in a mode its underlying driver supports — asking for ReadWriteMany on AWS EBS will never bind, because block storage cannot do it; that is what EFS, NFS, or CephFS are for.
Why is volumeBindingMode: WaitForFirstConsumer recommended for zonal block storage such as AWS EBS?
A StatefulSet with three replicas is scaled down to one. What happens to the PersistentVolumeClaims of the two removed Pods?
What does a CSI VolumeSnapshot guarantee about the data it captures?