Volume types

Volume TypeLifetimeUse Case
emptyDirPod lifetimeScratch space, share data between containers
hostPathNode lifetimeAccess node filesystem (logs, Docker socket): avoid in prod
configMapExternalMount config files
secretExternalMount sensitive data as files
PVCIndependentDurable storage across pod restarts
  • Two steps: spec.volumes[] + spec.containers[].volumeMounts[] (match by name)

emptyDir

volumes:
- name: logs-volume
  emptyDir: {}
volumeMounts:
- mountPath: /var/log/nginx
  name: logs-volume

ConfigMap as volume

volumes:
- name: config-vol
  configMap:
    name: app-config
    items:
    - key: app.properties
      path: application.properties   # filename inside mountPath
volumeMounts:
- name: config-vol
  mountPath: /etc/config

# subPath: mount one key as a single file
volumeMounts:
- name: config-vol
  mountPath: /etc/app/config.yaml
  subPath: config.yaml
  readOnly: true

Secret as volume

volumes:
- name: secret-vol
  secret:
    secretName: my-secret     # note: secretName (not name)
    defaultMode: 0400
    items:
    - key: tls.crt
      path: server.crt
volumeMounts:
- name: secret-vol
  mountPath: /etc/tls
  readOnly: true

Projected volume

volumes:
- name: projected
  projected:
    sources:                  # 'sources' not 'source': common mistake
    - configMap:
        name: app-config
    - secret:
        name: app-secret
    - serviceAccountToken:
        path: token
        expirationSeconds: 3600

PV + PVC + Pod

# 1. PersistentVolume (cluster admin creates)
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-data
spec:
  capacity:
    storage: 5Gi
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  hostPath:
    path: /data/pv
---
# 2. PersistentVolumeClaim (developer creates)
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: my-pvc
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 2Gi
  storageClassName: ""   # empty = use static PV; omit = use default StorageClass
---
# 3. Pod uses PVC
spec:
  volumes:
  - name: data
    persistentVolumeClaim:
      claimName: my-pvc
  containers:
  - name: app
    volumeMounts:
    - name: data
      mountPath: /data

Access modes

ModeAbbrMeaning
ReadWriteOnceRWOOne node can read+write
ReadOnlyManyROXMany nodes can read
ReadWriteManyRWXMany nodes can read+write
ReadWriteOncePodRWOPOnly one pod (k8s 1.22+)

StorageClass

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast
provisioner: kubernetes.io/no-provisioner
reclaimPolicy: Delete
volumeBindingMode: WaitForFirstConsumer

# PVC referencing it:
spec:
  storageClassName: fast
  accessModes: [ReadWriteOnce]
  resources:
    requests:
      storage: 10Gi

kubectl

kubectl get pv
kubectl get pvc
kubectl get sc
kubectl describe pvc <name>    # check binding status

Resize a PVC

kubectl patch pvc my-pvc -p '{"spec":{"resources":{"requests":{"storage":"2Gi"}}}}'
kubectl get pvc my-pvc

Exam gotchas

  • PVC binding: access modes + capacity must match PV (PVC <= PV)
  • hostPath data does not follow Pod to another node
  • StatefulSet delete → PVCs remain
  • ConfigMap volume: configMap.name vs Secret: secret.secretName
  • Projected: sources is a list; source vs sources fails validation
  • Reclaim: Retain = data kept, manual reclaim; Delete = PV + storage deleted
  • WaitForFirstConsumer → PV not created until Pod using PVC is scheduled