| Tested on | Rocky Linux 10.2 (Red Quartz) workstation |
|---|---|
| Package | kubectl 1.36.3 |
| Applies to | Any host with kubectl configured; Kubernetes cluster with Linux worker nodes; hostPath volumes must be permitted |
| Cert prep | CKA · CKAD |
| Lab environment | Multi-node kubeadm cluster with containerd — install Kubernetes with kubeadm |
| Privilege | Normal user (no sudo required on the workstation) |
| Scope | Pod-level volumes, volumeMounts, emptyDir, ConfigMap and Secret mounts, projected volumes, hostPath, multi-container sharing, subPath overview, lifetime comparison, inspection, and common volume problems. Does not cover PersistentVolume administration, StorageClasses, CSI drivers, StatefulSet volumeClaimTemplates, full ConfigMap or Secret management, init containers, or SecurityContext depth. |
This walkthrough uses the vol-lab namespace. You will mount Pod-level volumes into containers, share an emptyDir between two containers, mount ConfigMap and Secret keys as files, combine sources with a projected volume, and inspect mounts with kubectl describe and kubectl exec.
What Is a Kubernetes Volume?
A container filesystem comes from its image and is isolated from other containers. A Kubernetes volume is defined at the Pod level and shared according to the volume type.
- Multiple containers in the same Pod can mount the same volume
- Lifetime and persistence depend on the volume type, not on a single container restart
- Containers access volumes through
volumeMounts
Pod volumes suit scratch space, mounted configuration, credentials, and node-local paths. Cluster storage that survives Pod deletion belongs in PersistentVolume workflows.
Create a Pod with volumes and volumeMounts
Defining a volume under spec.volumes does not mount it automatically. Each container that needs the data must declare a matching volumeMounts entry.
Save this minimal manifest as volume-demo.yaml:
apiVersion: v1
kind: Pod
metadata:
name: volume-demo
namespace: vol-lab
spec:
containers:
- name: app
image: busybox:1.36
command: ["sh", "-c", "echo demo > /data/hello.txt && cat /data/hello.txt && sleep 3600"]
volumeMounts:
- name: scratch
mountPath: /data
volumes:
- name: scratch
emptyDir: {}The volume name scratch in volumes must match volumeMounts.name. mountPath is where the volume appears inside the container. Add readOnly: true when the container should not write to the mount.
Create the namespace and apply the Pod:
kubectl create namespace vol-labSample output:
namespace/vol-lab createdkubectl apply -f volume-demo.yamlSample output:
pod/volume-demo createdkubectl wait --for=condition=Ready pod/volume-demo -n vol-lab --timeout=60sSample output:
pod/volume-demo condition metkubectl exec volume-demo -n vol-lab -- sh -c 'until [ -s /data/hello.txt ]; do sleep 1; done; cat /data/hello.txt'Sample output:
demoThe mounted file /data/hello.txt contains the data written on the emptyDir volume.
Use emptyDir Volumes
Kubernetes creates an emptyDir volume when the Pod is scheduled to a node. All containers in the Pod can mount it. An individual container restart does not delete the Pod's emptyDir. The data is removed when that Pod is deleted or removed from its node. A replacement Pod, even with the same name, receives a new emptyDir.
Share an emptyDir between containers
Save this manifest as shared-emptydir.yaml. This Pod writes a file in one container and reads it in another:
apiVersion: v1
kind: Pod
metadata:
name: shared-emptydir
namespace: vol-lab
spec:
containers:
- name: writer
image: busybox:1.36
command: ["sh", "-c", "echo shared-data > /data/message.txt && sleep 3600"]
volumeMounts:
- name: scratch
mountPath: /data
- name: reader
image: busybox:1.36
command:
- sh
- -c
- |
until [ -s /data/message.txt ]; do
sleep 1
done
cat /data/message.txt
sleep 3600
volumeMounts:
- name: scratch
mountPath: /data
volumes:
- name: scratch
emptyDir: {}Apply the shared Pod after deleting the demo Pod:
kubectl delete pod volume-demo -n vol-lab --ignore-not-found=true --wait=truekubectl apply -f shared-emptydir.yamlkubectl wait --for=condition=Ready pod/shared-emptydir -n vol-lab --timeout=60sVerify the shared file in the reader container:
kubectl exec shared-emptydir -n vol-lab -c reader -- sh -c 'until [ -s /data/message.txt ]; do sleep 1; done; cat /data/message.txt'Sample output:
shared-dataBoth containers mounted the same Pod volume name scratch. Containers can mount the same volume at different paths or with different permissions: one container can use readOnly: true while another writes files. For fuller multi-container patterns, see Kubernetes sidecar containers.
Use a memory-backed emptyDir
Set medium: Memory to store data in node memory instead of disk:
volumes:
- name: scratch
emptyDir:
medium: Memory
sizeLimit: 64Mimedium: Memory creates a tmpfs-backed volume. Files written there count against the memory usage of the container that writes them. Set sizeLimit so a cache cannot grow toward the node's available memory.
Mount Kubernetes Objects as Files
ConfigMap, Secret, and projected volumes expose Kubernetes objects as files inside a container. Each key normally becomes one file in the mount directory.
Mount a ConfigMap
Create a ConfigMap and mount it as files under a directory.
kubectl create configmap app-config -n vol-lab --from-literal=app.properties="log.level=info"Sample output:
configmap/app-config createdSave config-vol.yaml:
apiVersion: v1
kind: Pod
metadata:
name: config-vol
namespace: vol-lab
spec:
containers:
- name: app
image: busybox:1.36
command: ["sh", "-c", "cat /config/app.properties && sleep 3600"]
volumeMounts:
- name: config
mountPath: /config
readOnly: true
volumes:
- name: config
configMap:
name: app-configkubectl apply -f config-vol.yamlkubectl wait --for=condition=Ready pod/config-vol -n vol-lab --timeout=60skubectl exec config-vol -n vol-lab -- cat /config/app.propertiesSample output:
log.level=infoEach ConfigMap key becomes a file. The filename normally matches the key. Use items under configMap to select keys or change target paths. Mounted ConfigMap and projected-volume data is exposed as files, and applications must reread those files to observe later updates. ConfigMap creation and environment-variable usage are covered in Kubernetes ConfigMaps.
Mount a Secret
Secrets mount the same way as ConfigMaps, with decoded values exposed as file contents.
kubectl create secret generic app-secret -n vol-lab --from-literal=db-password=supersecretSave secret-vol.yaml:
apiVersion: v1
kind: Pod
metadata:
name: secret-vol
namespace: vol-lab
spec:
containers:
- name: app
image: busybox:1.36
command: ["sh", "-c", "cat /secrets/db-password && sleep 3600"]
volumeMounts:
- name: creds
mountPath: /secrets
readOnly: true
volumes:
- name: creds
secret:
secretName: app-secretkubectl apply -f secret-vol.yamlkubectl wait --for=condition=Ready pod/secret-vol -n vol-lab --timeout=60skubectl exec secret-vol -n vol-lab -- cat /secrets/db-passwordSample output:
supersecretThe file db-password contains the decoded Secret value. Secret creation and rotation workflows live in Kubernetes Secrets.
Combine sources with a projected volume
A projected volume combines several supported sources into one directory. Supported sources include ConfigMap, Secret, and ServiceAccount token. Downward API field and resource references are another supported source; see container command, args, and environment for those projections.
ConfigMap and Secret sources referenced by a projected volume must exist in the same namespace as the Pod. Kubernetes requires all projected sources to be in the Pod's namespace.
Save projected-vol.yaml:
apiVersion: v1
kind: Pod
metadata:
name: projected-vol
namespace: vol-lab
spec:
containers:
- name: app
image: busybox:1.36
command: ["sh", "-c", "sleep 3600"]
volumeMounts:
- name: all
mountPath: /all
readOnly: true
volumes:
- name: all
projected:
sources:
- configMap:
name: app-config
- secret:
name: app-secretkubectl apply -f projected-vol.yamlkubectl wait --for=condition=Ready pod/projected-vol -n vol-lab --timeout=60skubectl exec projected-vol -n vol-lab -- sh -c 'printf "ConfigMap: "; cat /all/app.properties; printf "\nSecret: "; cat /all/db-password; printf "\n"'Sample output:
ConfigMap: log.level=info
Secret: supersecretFiles from the ConfigMap and Secret share one mount directory. Avoid duplicate target paths. Each source can list items to select specific keys.
Mount one file with subPath
subPath mounts one file or subdirectory from a volume instead of replacing the entire mountPath directory. Use it when you must preserve existing image files under the parent directory or mount a single ConfigMap or Secret key.
volumeMounts:
- name: config
mountPath: /etc/app.properties
subPath: app.properties
readOnly: trueSingle-file mounts, directory preservation, update limits, and permission pitfalls are covered in Kubernetes subPath examples. This article does not duplicate that guide.
Use a hostPath Volume
A hostPath volume mounts a file or directory from the node filesystem into the Pod. Data layout differs between nodes, and production clusters often restrict writable hostPath mounts through admission policy. Kubernetes documents hostPath as node-dependent and recommends read-only mounts where possible.
Save hostpath-vol.yaml:
apiVersion: v1
kind: Pod
metadata:
name: hostpath-vol
namespace: vol-lab
spec:
os:
name: linux
nodeSelector:
kubernetes.io/os: linux
containers:
- name: app
image: busybox:1.36
command:
- sh
- -c
- cat /node-hostname && sleep 3600
volumeMounts:
- name: hostdata
mountPath: /node-hostname
readOnly: true
volumes:
- name: hostdata
hostPath:
path: /etc/hostname
type: FileThe command reads the node hostname from /etc/hostname without changing the node filesystem.
kubectl apply -f hostpath-vol.yamlkubectl wait --for=condition=Ready pod/hostpath-vol -n vol-lab --timeout=60skubectl exec hostpath-vol -n vol-lab -- cat /node-hostnameSample output (hostname varies by node):
worker01Compare Volume Lifetime and Use Cases
Container restart and Pod deletion are different events. An individual container restart does not delete a Pod's emptyDir. Deleting the Pod or removing it from its node removes that emptyDir. A replacement Pod, even with the same name, receives a new emptyDir.
| Volume type | Data source | Data after container restart | After Pod deletion |
|---|---|---|---|
emptyDir |
Pod-local directory | Retained | Data removed |
| ConfigMap | Kubernetes API object | Files remain available | Mount removed; ConfigMap remains |
| Secret | Kubernetes API object | Files remain available | Mount removed; Secret remains |
| Projected | Multiple Kubernetes sources | Files remain available | Mount removed; source objects remain |
hostPath |
Node filesystem | Retained | Data normally remains on that node |
| Requirement | Volume type |
|---|---|
| Temporary shared files inside one Pod | emptyDir |
| Mount application configuration | ConfigMap volume |
| Mount credentials or sensitive files | Secret volume |
| Combine several configuration sources | Projected volume |
| Access a node-local path | hostPath |
| Retain data independently of the Pod | PersistentVolume / PVC |
Cluster storage that survives Pod deletion is covered in PersistentVolume and PVC.
Inspect and Troubleshoot Mounted Volumes
View volume and mount definitions on the Pod:
kubectl get pod config-vol -n vol-lab -o yamlspec.volumes and volumeMounts appear under the Pod spec and each container.
kubectl describe pod summarizes mounts:
kubectl describe pod config-vol -n vol-labSample output excerpt:
Mounts:
/config from config (ro)List a mounted file inside the container:
kubectl exec config-vol -n vol-lab -- ls -l /config/app.propertiesSample output:
lrwxrwxrwx 1 root root 21 Jul 26 08:20 /config/app.properties -> ..data/app.properties| Symptom | Likely cause | Fix |
|---|---|---|
| Volume mount fails | volumeMounts.name does not match volumes.name |
Use the same name string in both places |
| ConfigMap or Secret file missing | Wrong object name, namespace, key, or items mapping |
Verify the object with kubectl get configmap or kubectl get secret; fix items paths |
Image files disappeared at mountPath |
Whole directory replaced by the mount | Use a different mountPath or mount one file with subPath |
| Another container cannot see shared files | Containers mount different volume names or paths | Confirm both use the same Pod volumes.name |
emptyDir data gone |
Pod was deleted or removed from its node | Expected for emptyDir; use PVC-backed storage for persistence |
hostPath differs on another node |
Each node has its own filesystem | Do not treat hostPath as portable shared storage |
What's Next
- Kubernetes PersistentVolume and PVC with Examples
- Kubernetes StorageClass and Dynamic Volume Provisioning
- Kubernetes subPath Volume Mounts with Examples
References
- Volumes — Kubernetes documentation
- ConfigMap — Kubernetes documentation
- Secrets — Kubernetes documentation
Summary
You defined volumes on the Pod, mounted them with volumeMounts, and matched volume names to mount entries. emptyDir gave two containers a shared scratch directory that outlived a container restart but not Pod deletion. ConfigMap and Secret volumes exposed keys as read-only files, and a projected volume merged both into one directory.
Mounting at mountPath replaces the directory view at that path, which is why missing image files often mean you need a different path or subPath. hostPath ties the Pod to a specific node path and is not portable cluster storage. When data must survive Pod replacement, move to PersistentVolume and PVC workflows instead of extending Pod volumes.

