Kubernetes Volumes with Practical Examples

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.

IMPORTANT
This article covers Pod volumes defined in Pod YAML. It does not cover PersistentVolume provisioning, StorageClasses, or dynamic storage. Use a PersistentVolume and PVC when application-generated files or state must survive Pod deletion or replacement.

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:

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:

bash
kubectl create namespace vol-lab

Sample output:

output
namespace/vol-lab created
bash
kubectl apply -f volume-demo.yaml

Sample output:

output
pod/volume-demo created
bash
kubectl wait --for=condition=Ready pod/volume-demo -n vol-lab --timeout=60s

Sample output:

output
pod/volume-demo condition met
bash
kubectl exec volume-demo -n vol-lab -- sh -c 'until [ -s /data/hello.txt ]; do sleep 1; done; cat /data/hello.txt'

Sample output:

output
demo

The 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:

yaml
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:

bash
kubectl delete pod volume-demo -n vol-lab --ignore-not-found=true --wait=true
bash
kubectl apply -f shared-emptydir.yaml
bash
kubectl wait --for=condition=Ready pod/shared-emptydir -n vol-lab --timeout=60s

Verify the shared file in the reader container:

bash
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:

output
shared-data

Both 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:

yaml
volumes:
  - name: scratch
    emptyDir:
      medium: Memory
      sizeLimit: 64Mi

medium: 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.

bash
kubectl create configmap app-config -n vol-lab --from-literal=app.properties="log.level=info"

Sample output:

output
configmap/app-config created

Save config-vol.yaml:

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-config
bash
kubectl apply -f config-vol.yaml
bash
kubectl wait --for=condition=Ready pod/config-vol -n vol-lab --timeout=60s
bash
kubectl exec config-vol -n vol-lab -- cat /config/app.properties

Sample output:

output
log.level=info

Each 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.

bash
kubectl create secret generic app-secret -n vol-lab --from-literal=db-password=supersecret

Save secret-vol.yaml:

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-secret
bash
kubectl apply -f secret-vol.yaml
bash
kubectl wait --for=condition=Ready pod/secret-vol -n vol-lab --timeout=60s
bash
kubectl exec secret-vol -n vol-lab -- cat /secrets/db-password

Sample output:

output
supersecret

The 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:

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-secret
bash
kubectl apply -f projected-vol.yaml
bash
kubectl wait --for=condition=Ready pod/projected-vol -n vol-lab --timeout=60s
bash
kubectl exec projected-vol -n vol-lab -- sh -c 'printf "ConfigMap: "; cat /all/app.properties; printf "\nSecret: "; cat /all/db-password; printf "\n"'

Sample output:

output
ConfigMap: log.level=info
Secret: supersecret

Files 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.

yaml
volumeMounts:
  - name: config
    mountPath: /etc/app.properties
    subPath: app.properties
    readOnly: true

Single-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:

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: File

The command reads the node hostname from /etc/hostname without changing the node filesystem.

bash
kubectl apply -f hostpath-vol.yaml
bash
kubectl wait --for=condition=Ready pod/hostpath-vol -n vol-lab --timeout=60s
bash
kubectl exec hostpath-vol -n vol-lab -- cat /node-hostname

Sample output (hostname varies by node):

output
worker01

Compare 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:

bash
kubectl get pod config-vol -n vol-lab -o yaml

spec.volumes and volumeMounts appear under the Pod spec and each container.

kubectl describe pod summarizes mounts:

bash
kubectl describe pod config-vol -n vol-lab

Sample output excerpt:

output
Mounts:
  /config from config (ro)

List a mounted file inside the container:

bash
kubectl exec config-vol -n vol-lab -- ls -l /config/app.properties

Sample output:

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


References


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.


Frequently Asked Questions

1. Where do I define a Kubernetes volume?

Volumes are defined under spec.volumes on the Pod. Each container that needs the data adds a volumeMount with the same volume name, a mountPath, and optional readOnly or subPath fields.

2. Does emptyDir data survive a container restart?

Yes. 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.

3. How do ConfigMap and Secret volumes appear inside a container?

Each selected key becomes a file in the mount directory. ConfigMap values are plain text files. Secret values are decoded bytes exposed as file contents, not base64 text in the file.

4. Why did files disappear after I added a volumeMount?

Mounting a volume at a path replaces the directory view at that mountPath. Image files under that path are hidden unless you mount a single file with subPath or choose a different mountPath.

5. When should I use a PersistentVolume instead of these Pod volumes?

Use Pod volumes for scratch space, mounted configuration, credentials, and node-local paths. Use a PersistentVolume and PersistentVolumeClaim when application-generated files or state must survive Pod deletion or replacement.
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels across development, DevOps, networking, and security, delivering robust and efficient solutions for diverse projects.

  • Go (programming language)
  • Python (programming language)
  • DevOps
  • Computer Security
  • Cloud Computing
  • Kubernetes
  • Linux
  • Ansible (software)