Volumes and Ephemeral Storage

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production; see the section bibliography for the reference material consulted while preparing these pages.

Containers are disposable: a container’s writable layer disappears when it is restarted, and a Pod’s storage vanishes when the Pod is deleted or evicted. A Kubernetes volume solves two problems at once: it survives container restarts within a Pod, and it lets containers in a Pod share files. This page covers volumes whose lifetime is bounded by the Pod — "ephemeral storage". Storage that outlives Pods is in Persistent Volumes and Storage Classes. This page is written against Kubernetes 1.37; see the section index for the version baseline.

Container filesystem versus Pod volume

Rensin’s 2015 book already draws the line that still defines the model: a container volume (Docker’s VOLUME, a bind-mounted host path) belongs to one container and is tied to the host, whereas a Pod volume is declared once in spec.volumes and mounted by name into any container of the Pod through volumeMounts, so containers can share it and the orchestrator can decide where it is backed. The book’s list (EmptyDir, NFS, GCEPersistentDisk) shows both halves of this page: emptyDir is the ephemeral one, while NFS and cloud disks are persistent storage. Two warnings have aged: the in-tree gcePersistentDisk and similar cloud-provider volume types were removed in favour of CSI drivers, and NFS is now normally consumed through a PersistentVolume.

Every container also has a writable layer of its own. It is slow, is lost on container restart, and counts against the node’s ephemeral storage, so data that must outlive a restart goes in a volume.

emptyDir

An emptyDir is created empty when the Pod is assigned to a node and deleted with the Pod. It survives container crashes, which makes it the standard scratch space, cache and inter-container hand-off (for example a sidecar writing files that the main container serves).

apiVersion: v1
kind: Pod
metadata:
  name: catalog-api-with-sidecar
  namespace: bookshelf
spec:
  containers:
    - name: catalog-api
      image: ghcr.io/example/bookshelf-catalog-api:1.0.0
      volumeMounts:
        - name: scratch
          mountPath: /tmp/work
        - name: shared-logs
          mountPath: /var/log/catalog
    - name: log-forwarder
      image: ghcr.io/example/bookshelf-log-forwarder:1.0.0
      volumeMounts:
        - name: shared-logs
          mountPath: /logs
          readOnly: true
  volumes:
    - name: scratch
      emptyDir:
        sizeLimit: 500Mi
    - name: shared-logs
      emptyDir:
        medium: Memory        # tmpfs; counts against container memory limits
        sizeLimit: 64Mi
  • medium: Memory backs the volume with RAM (tmpfs): fast and never written to disk, but its usage is charged to the container memory limit (see Resource Management).

  • sizeLimit bounds it; the kubelet evicts the Pod if the limit is exceeded. Without a limit on the default medium, a runaway writer fills the node’s disk.

  • An emptyDir is also how init containers pass prepared files to the main containers (Health Probes and Container Lifecycle).

hostPath and its risks

A hostPath volume mounts a file or directory of the node’s filesystem into the Pod. It is indispensable for node agents (the log agent in DaemonSets reads /var/log/pods) and dangerous everywhere else: the Pod’s data is bound to one node, contents differ per node, and a writable mount of a sensitive path (the container runtime socket, /etc, kubelet directories) is a container-escape primitive. Pod Security forbids hostPath already at the baseline level (and therefore also at restricted) (Pod Security). If it is unavoidable, mount the narrowest path, readOnly: true, and set the type check.

  volumes:
    - name: pod-logs
      hostPath:
        path: /var/log/pods
        type: Directory       # fail if the path is missing or is not a directory

Source: Kubernetes Docs — hostPath. For single-node development prefer a local PersistentVolume, see Persistent Volumes and Storage Classes.

Volumes backed by API objects and projected volumes

configMap, secret and downwardAPI volumes (ConfigMaps, Secrets) expose API data as files. A projected volume merges several of these sources into a single directory, so an application sees one mount point:

  containers:
    - name: catalog-api
      image: ghcr.io/example/bookshelf-catalog-api:1.0.0
      volumeMounts:
        - name: combined
          mountPath: /etc/catalog
          readOnly: true
  volumes:
    - name: combined
      projected:
        sources:
          - configMap:
              name: catalog-config
          - secret:
              name: catalog-db
              items:
                - key: DB_USER
                  path: db/user
          - downwardAPI:
              items:
                - path: labels
                  fieldRef:
                    fieldPath: metadata.labels
          - serviceAccountToken:
              path: api-audience-token     # short-lived, audience-bound
              audience: reviews-api
              expirationSeconds: 3600

The serviceAccountToken source is how Pods obtain short-lived, audience-scoped credentials instead of the legacy long-lived token Secrets; see Authentication, Authorization and RBAC.

Generic and CSI ephemeral volumes

Two inline forms give a Pod its own provisioned volume that is created and deleted with it:

  • Generic ephemeral volume (ephemeral.volumeClaimTemplate): the Pod controller creates a PersistentVolumeClaim owned by the Pod, so any StorageClass can provide per-Pod scratch space larger or faster than emptyDir, with capacity tracking, snapshots and resizing.

  • CSI ephemeral volume (csi inline): a CSI driver that supports Ephemeral mode creates the volume at Pod start from inline attributes; used by drivers such as the Secrets Store CSI driver.

  volumes:
    - name: scratch-fast
      ephemeral:
        volumeClaimTemplate:
          metadata:
            labels:
              app: catalog-api
          spec:
            accessModes: ["ReadWriteOnce"]
            storageClassName: standard
            resources:
              requests:
                storage: 2Gi

Image volumes

An image volume mounts the content of an OCI image or artifact, read-only, directly as a volume. It separates data from the application image: ship model files, static assets or configuration bundles as their own versioned artifacts from a registry, without baking them into the application image or copying them with an init container. The feature graduated to stable in 1.36; verify against the docs for your cluster version and container runtime, as runtime support is required.

  containers:
    - name: web
      image: ghcr.io/example/bookshelf-web:1.0.0
      volumeMounts:
        - name: static-assets
          mountPath: /usr/share/assets
  volumes:
    - name: static-assets
      image:
        reference: ghcr.io/example/bookshelf-assets:2026.09
        pullPolicy: IfNotPresent

subPath, readOnly and mount propagation

  • subPath mounts a single file or subdirectory of a volume instead of its root, so several containers (or several mounts) can use different parts of one volume. Remember that subPath mounts of ConfigMap and Secret volumes do not receive updates. subPathExpr builds the path from environment variables such as the Pod name.

  • readOnly: true on a volumeMount makes the mount read-only for that container; use it everywhere a container does not need to write. recursiveReadOnly: Enabled also makes submounts read-only.

  • mountPropagation controls whether mounts made inside a container become visible on the host or in other containers. None is the default; HostToContainer lets a node agent see mounts the host adds later; Bidirectional also propagates container mounts to the host and requires a privileged container, so it is reserved for storage drivers.

Local ephemeral storage requests and limits

Container writable layers, logs and emptyDir volumes on the default medium all consume the node’s local ephemeral storage. It is a resource like CPU and memory: ephemeral-storage can be requested (used by the scheduler) and limited (enforced by the kubelet, which evicts a Pod that exceeds it).

      resources:
        requests:
          ephemeral-storage: 1Gi
        limits:
          ephemeral-storage: 2Gi

Combined with sizeLimit on emptyDir, this keeps a misbehaving Pod from exhausting the node’s disk; quotas and LimitRanges extend the same limits per namespace (Resource Management).