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.
Last updated: August 2026

8.2 Volume Types, Snapshots & Stateful Storage Operations

Quick Answer: Not every volume is a PersistentVolumeClaim. emptyDir is scratch space that dies with the Pod; hostPath mounts a node directory and is a privilege-escalation hazard; projected merges 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: WaitForFirstConsumer binding, 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

TypeLifetimeTypical useCaution
emptyDirPodScratch space, caches, sharing files between containers in a PodDeleted when the Pod is removed or rescheduled — not just on container restart
emptyDir with medium: MemoryPodRAM-backed tmpfs for very fast temporary dataCounts against the container's memory limit; a large write can trigger an OOM kill
hostPathNodeNode 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 / secretPodInjecting configuration and credentials as files~1 MiB limit; Secrets are held in tmpfs
projectedPodOne mount combining several sourcesSee below
downwardAPIPodExposing Pod name, namespace, labels, and resource limits as filesRead-only
Generic ephemeral volumePodA real CSI volume that should be created and destroyed with the PodUses a StorageClass, unlike emptyDir
persistentVolumeClaimIndependent of the PodDatabases, queues, uploads — anything that must surviveCovered 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:

ModeBehaviour
ImmediateThe PersistentVolume is provisioned as soon as the PVC is created
WaitForFirstConsumerProvisioning 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:

  1. The StorageClass must set allowVolumeExpansion: true.
  2. 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:

ObjectRoleAnalogy
VolumeSnapshotClassWhich driver takes the snapshot and with what parametersStorageClass
VolumeSnapshotA namespaced request for a point-in-time copy of a PVCPersistentVolumeClaim
VolumeSnapshotContentThe cluster-scoped object representing the actual snapshotPersistentVolume
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:

  1. Scaling down does not delete the PVC. Scaling from 3 to 1 removes Pods orders-db-2 and orders-db-1 but 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.
  2. Deleting the StatefulSet does not delete the PVCs either, unless a persistentVolumeClaimRetentionPolicy says otherwise.
  3. volumeClaimTemplates is 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.

Test Your Knowledge

Why is volumeBindingMode: WaitForFirstConsumer recommended for zonal block storage such as AWS EBS?

A
B
C
D
Test Your Knowledge

A StatefulSet with three replicas is scaled down to one. What happens to the PersistentVolumeClaims of the two removed Pods?

A
B
C
D
Test Your Knowledge

What does a CSI VolumeSnapshot guarantee about the data it captures?

A
B
C
D