Volumes and Volume Types
The idea in one sentence
Section titled “The idea in one sentence”A Volume is a directory accessible to the containers in a Pod, and its lifecycle is tied to the Pod, not to any single container inside it.
Why a container’s own filesystem is not enough
Section titled “Why a container’s own filesystem is not enough”Every container gets its own writable layer, but that layer is thrown away the moment the container restarts — a crash, an image update, or a liveness-probe failure wipes it clean. That is fine for stateless application code, but it breaks anything that needs to survive a restart or be shared between containers in the same Pod. A Volume solves both problems: it is declared once at the Pod level, and it keeps existing for as long as the Pod exists, independent of how many times an individual container inside that Pod restarts.
# The container restarts, but the Pod (and its volumes) does not disappearkubectl get pod log-processor -o jsonpath='{.status.containerStatuses[*].restartCount}'Common volume types
Section titled “Common volume types”emptyDir— an empty directory created when the Pod starts. It is scratch space: good for caches, or for sharing files between a main container and a sidecar. It is deleted permanently when the Pod is removed, not just when a container restarts.hostPath— mounts a path from the underlying node’s filesystem straight into the Pod. It is powerful, but risky: it ties the Pod to whatever data happens to live on that specific node, and it can expose sensitive parts of the node’s filesystem to a container. Use it sparingly, and only when you actually need node-local access (log collectors, node-level agents).configMap/secretvolumes — project the key-value data from a ConfigMap or Secret into the Pod as files, one file per key. Covered in depth in the next lesson on ConfigMaps and Secrets.- Projected volumes — combine several sources (ConfigMap, Secret, downwardAPI, serviceAccountToken) into a single mounted directory, so a container only needs one mount point to see data that actually comes from multiple objects.
Here is a Pod where two containers share one emptyDir volume — a common pattern for a main application container paired with a log-shipping sidecar:
apiVersion: v1kind: Podmetadata: name: log-processorspec: volumes: - name: shared-logs emptyDir: {} containers: - name: app image: my-app:1.0 volumeMounts: - name: shared-logs mountPath: /var/log/app - name: log-shipper image: fluent-bit:2.2 volumeMounts: - name: shared-logs mountPath: /var/log/app readOnly: trueAnd a hostPath example, mounting a node’s /proc directory read-only into a monitoring agent:
apiVersion: v1kind: Podmetadata: name: node-exporterspec: volumes: - name: proc hostPath: path: /proc type: Directory containers: - name: node-exporter image: prom/node-exporter:v1.7.0 volumeMounts: - name: proc mountPath: /host/proc readOnly: trueDeclaring a volume is always a two-step process
Section titled “Declaring a volume is always a two-step process”Naming a volume is not enough to use it. Every Pod-level volume needs two matching pieces:
spec.volumes[]— declares the volume and where its data comes from (emptyDir,hostPath,configMap,persistentVolumeClaim, and so on). This is Pod-scoped.- Each container’s
spec.containers[].volumeMounts[]— mounts that already-declared volume into that specific container’s filesystem, at a chosenmountPath.
A volume declared in spec.volumes but never referenced in a container’s volumeMounts does nothing for that container — it simply is not mounted there. This is also how two containers in the same Pod can share data: both list the same volume name in their own volumeMounts, pointing at the one shared directory declared once in spec.volumes.
# Confirm both containers see the same file through the shared emptyDirkubectl exec log-processor -c log-shipper -- ls /var/log/appflowchart LR
subgraph pod["Pod: log-processor"]
a["Container: app"] -->|writes| vol[("emptyDir volume: shared-logs")]
vol -->|reads| b["Container: log-shipper"]
end