Storage: Volumes, Bind Mounts and tmpfs

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.

A container’s writable layer lives and dies with the container, so anything that must survive a restart, an upgrade or a docker rm needs to live somewhere else. Docker offers three kinds of mount for that — volumes, bind mounts and tmpfs — plus image mounts for read-only content. Learning Docker (chapter 7) explains volumes and bind mounts in terms that still apply; this page brings them up to date for Docker Engine 29.8.x and Docker Desktop 4.91 or later.

The writable layer is ephemeral

Every file a container writes outside a mount lands in its writable layer (see Images, Layers and Tags). That layer is fine for scratch data, but it is slower than a mount (writes go through the storage driver’s copy-on-write), it is tied to one container, and it is deleted with the container. Databases, uploaded files and anything else you want to keep belong in a mount.

Volume and bind mount and tmpfs compared: a volume lives in a Docker-managed area and a bind mount maps a host path and tmpfs lives in host memory
Volume Bind mount tmpfs

Where the data lives

A Docker-managed directory (/var/lib/docker/volumes/…​) or a volume driver’s backend

Any path on the host you choose

Host memory only

Created by

docker volume create, or automatically on first use

You — the host path must exist (with --mount)

Docker, per container

Survives docker rm

Yes

Yes (it is your host directory)

No

Shareable between containers

Yes

Yes

No

Typical use

Database files, persistent application data

Source code in development, configuration files

Secrets or scratch data that must not touch disk

Derived from Storage.

Volumes

Volumes are the recommended way to persist data. Docker creates and manages them, they work the same on Linux, Docker Desktop and in Compose, they can be backed up and migrated, and volume drivers can place them on NFS, CIFS or cloud storage.

docker volume create orders-db-data
docker run -d --name orders-db \
  -e POSTGRES_PASSWORD=dev-only-password \
  --mount type=volume,src=orders-db-data,dst=/var/lib/postgresql \
  postgres:18.1
docker volume ls
docker volume inspect orders-db-data          # driver, mountpoint, labels
docker rm -f orders-db && docker volume ls    # the volume -- and the data -- is still there
docker volume rm orders-db-data               # only when no container uses it
docker volume prune                           # remove unused anonymous volumes (add --all for named ones)

Derived from Volumes. The password is a development-only placeholder.

  • Named vs. anonymous: -v /data without a name creates an anonymous volume with a random ID — hard to find again and removed by docker run --rm together with the container. Name the volumes you care about.

  • Pre-population: when an empty volume is mounted over a directory that contains files in the image, Docker copies those files into the volume first (bind mounts do not do this).

  • Read-only: --mount type=volume,src=config,dst=/etc/app,readonly (or -v config:/etc/app:ro).

Bind mounts

A bind mount maps a host file or directory into the container. It is the tool for development loops — edit on the host, see the change in the container — and for injecting a configuration file:

docker run --rm -it \
  --mount type=bind,src="$(pwd)",dst=/workspace \
  --mount type=bind,src="$(pwd)/config/application.yaml",dst=/app/config/application.yaml,readonly \
  -w /workspace eclipse-temurin:25-jdk ./mvnw -q -B verify

Derived from Bind mounts. Points to watch:

  • Permissions and UIDs: the container sees host files with their numeric owner. A process running as UID 10001 in the container cannot write to a host directory owned by UID 1000 unless permissions allow it; match the UIDs (-u "$(id -u):$(id -g)"), or rely on Docker Desktop, which maps ownership for you.

  • Read-only (readonly / :ro) for anything the container should not modify.

  • SELinux: on Fedora, RHEL and CentOS Stream hosts add :z (shared label) or :Z (private label) with -v so the container may access the files; never relabel system directories such as /home or /usr.

  • --mount vs. -v: -v host-path:container-path silently creates a missing host directory (owned by root); --mount type=bind fails instead, which is usually what you want. --mount is also more explicit (key=value pairs) and is the only syntax for some options, so prefer it in scripts.

tmpfs mounts

A tmpfs mount is backed by memory and disappears when the container stops — useful for secrets you do not want on disk and for scratch space in a container with a --read-only root filesystem:

docker run -d --read-only --mount type=tmpfs,dst=/tmp,tmpfs-size=64m,tmpfs-mode=1777 orders-service:1.4.0

Derived from tmpfs mounts. tmpfs mounts are Linux-only and cannot be shared between containers.

Image mounts

An image mount exposes the filesystem of another image read-only inside the container, without copying it into a layer — for example to bring debugging tools or static assets into a minimal container. Image mounts are no longer experimental as of Engine 29.7:

docker run --rm --mount type=image,source=busybox:musl,destination=/dbg alpine /dbg/bin/ls /dbg/bin

Derived from Image mounts.

Backing up and restoring a volume

A throwaway container that mounts both the volume and a host directory can archive or restore the data with ordinary tools. Stop the writer first (or use the database’s own dump tool) for a consistent copy.

sequenceDiagram participant Host as Host directory ./backup participant T as Throwaway container (ubuntu) participant V as Volume orders-db-data Note over V: docker stop orders-db T->>V: mount read-only at /data T->>Host: tar czf /backup/orders-db.tgz -C /data . Note over Host: orders-db.tgz stored off-host T->>V: (restore) mount volume new-data at /data Host->>T: tar xzf /backup/orders-db.tgz -C /data Note over V: docker run ... -v new-data:/var/lib/postgresql
docker stop orders-db
docker run --rm -v orders-db-data:/data:ro -v "$(pwd)/backup":/backup ubuntu:24.04 \
  tar czf /backup/orders-db.tgz -C /data .
docker start orders-db

# Restore into a fresh volume
docker volume create orders-db-restored
docker run --rm -v orders-db-restored:/data -v "$(pwd)/backup":/backup ubuntu:24.04 \
  tar xzf /backup/orders-db.tgz -C /data

Derived from Back up, restore, or migrate data volumes. The official example borrows another container’s mounts with --volumes-from; mounting the named volume directly, as above, is equivalent and clearer.

Docker Desktop: file sharing and synchronized file shares

On macOS and Windows, bind mounts cross the boundary into Docker Desktop’s Linux VM (see Docker Architecture and the OCI). Named volumes live inside the VM and are fast; bind-mounted host directories are shared through virtiofs (or the WSL 2 filesystem) and can be slow for large trees such as node_modules or a Maven reactor. Remedies, in order:

  1. Keep write-heavy directories (target/, node_modules, database files) in named volumes rather than bind mounts.

  2. On Windows, keep the source tree inside the WSL 2 filesystem, not on C:\.

  3. Use synchronized file shares, which keep a fast in-VM copy of a host directory synchronised in both directions (a Docker subscription feature).

  4. Use Compose Watch to sync files instead of bind-mounting them (see Compose in Practice).

Which host directories may be bind-mounted is controlled under Settings > Resources > File sharing.

Storage drivers vs. the containerd image store

Mounts bypass the storage layer entirely; the storage driver (overlay2) or containerd snapshotter only handles image layers and the writable layer. That distinction, and how Engine 29 changed the default, is described on Docker Architecture and the OCI.

Superseded: data-volume containers and --volumes-from

Learning Docker (chapter 7) and Docker: Up & Running describe data-volume containers: a stopped container created only to own volumes, which other containers attach with --volumes-from. That pattern predates named volumes (Docker 1.9) and is obsolete: create a named volume with docker volume create and mount it by name with --mount or in Compose. --volumes-from remains available for one-off tasks such as the backup above, but not as a way to organise persistent data.