Right, let’s talk about the stuff that doesn’t stick around. Ephemeral volumes are the sprinters of the Kubernetes storage world: blindingly fast, incredibly useful for a specific leg of the race, and then they vanish without a trace. They’re perfect for all the temporary, scratch-space, in-flight nonsense your application needs to do its job right now. Unlike their persistent cousins, these guys are tied to the lifecycle of a Pod. The Pod gets scheduled, the volume is created. The Pod dies, the volume gets deleted. Poof. It’s the ultimate “this meeting could have been an email” of storage—no permanent record.

We use these for things that are either a) not worth saving, or b) too sensitive to save for long. Think cache files, temporary build artifacts, or—crucially—injecting configuration data and secrets that you absolutely do not want written to a persistent disk somewhere.

The Classic Scratch Pad: emptyDir

The emptyDir is the simplest volume type. You ask for a blank directory, Kubernetes gives you one on the node hosting your Pod. It’s empty when it’s created. Shocking, I know.

Why would you use this? Scratch space for a disk-based sort operation. A place for a web server to unpack its theme assets on startup. A cache directory for a tool that downloads a bunch of files. The data is valuable for the life of the Pod, but afterwards, who cares?

Here’s the kicker: by default, this emptyDir lives on whatever medium backs the node’s root filesystem (probably a hard disk). But you can be fancy and tell Kubernetes to put it in the node’s RAM instead. This is a fantastic trick for when you need screaming fast I/O, but remember: it counts against your Pod’s memory limits. Run out of RAM, and your Pod gets OOMKilled. It’s a trade-off.

apiVersion: v1
kind: Pod
metadata:
  name: test-pod
spec:
  containers:
  - name: test-container
    image: alpine
    command: ["sh", "-c"]
    args:
    - while true; do
        echo "$(date) - Still alive!" >> /scratch-dir/log.txt;
        sleep 5;
      done
    volumeMounts:
    - name: scratch-volume
      mountPath: /scratch-dir
  volumes:
  - name: scratch-volume
    emptyDir:
      # Nothing here means it uses the node's default storage (disk)
      # To use RAM instead, uncomment the next line. Choose wisely.
      # medium: Memory

Injecting Configuration: configMap and secret Volumes

This is where ephemeral volumes get really powerful. You’ve defined your configuration in a ConfigMap or your sensitive data in a Secret. Now, instead of setting environment variables, you want to present this data as files in a directory. That’s what configMap and secret volume types are for.

Why files? Some applications are stubborn old mules and only read config from a .conf file in /etc/. This is how you feed them. It’s also brilliant for injecting an entire set of configuration files, not just individual values.

Kubernetes does something very clever here: it creates a magical read-only directory and symlinks all the keys from your ConfigMap or Secret as individual files. The content of the key is the content of the file. And here’s the best part: if you update the underlying ConfigMap or Secret, Kubernetes eventually updates the files in the volume. It’s not instantaneous, but it’s usually within a minute. This is a huge win for refreshing configuration without restarting your Pod.

apiVersion: v1
kind: Pod
metadata:
  name: configmap-pod
spec:
  containers:
  - name: myapp
    image: nginx
    volumeMounts:
    - name: app-config-volume
      mountPath: /etc/app-config
  volumes:
  - name: app-config-volume
    configMap:
      name: my-app-config  # Name of your existing ConfigMap
      # Optional: specify specific keys to include, otherwise it includes all.
      items:
      - key: "nginx.conf"
        path: "custom-nginx.conf" # This will be the filename in the volume
      defaultMode: 0644 # Set file permissions. For secrets, you'd want 0400.

The Overachiever: projected Volumes

Someone at Google looked at the configMap and secret volumes and thought, “What if we could mount several of these into the same directory?” Thus, the projected volume was born. It’s not a new source of data; it’s a meta-volume that combines multiple sources into one unified view.

This is incredibly useful for when you need to present a single directory to your application, but the contents of that directory come from different sources (e.g., a ConfigMap for general config, a Secret for TLS certificates, and maybe a downwardAPI volume for pod metadata).

The files are simply laid down on top of each other in the target path. Be warned: if two sources define a file with the same name, it’s last write wins. The order in the volume definition matters!

apiVersion: v1
kind: Pod
metadata:
  name: projected-pod
spec:
  containers:
  - name: myapp
    image: alpine
    command: ["sh", "-c", "ls -l /projected-volume && sleep 3600"]
    volumeMounts:
    - name: all-in-one-volume
      mountPath: /projected-volume
  volumes:
  - name: all-in-one-volume
    projected:
      sources:
      - configMap:
          name: my-configmap
      - secret:
          name: my-secret
          items:
            - key: username
              path: my-secret/user
      - downwardAPI:
          items:
            - path: "labels"
              fieldRef:
                fieldPath: metadata.labels

The Crucial Pitfall: Never forget these are ephemeral. The data vanishes when the Pod does. If your app writes anything important to a mounted configMap volume, it’s gone on a restart. Those volumes are a one-way street: from the API server into your Pod. Also, those file permissions matter. A Secret with too-open permissions (e.g., world-readable) is a security lapses waiting to happen. Always set defaultMode: 0400 on your secret volumes to keep prying eyes out. Use them for their intended purpose: temporary, injected data. For anything else, we need to talk about PersistentVolumes.