Skip to content
Blog

Docker Volumes vs Bind Mounts vs tmpfs: Where Your Data Should Live

Containers are ephemeral — your data shouldn't be. Compare Docker volumes, bind mounts, and tmpfs mounts, and learn exactly when to use each.

Published on • October 11, 2026

AI Assistant

The first time you docker rm a container with your database inside it, you learn a hard lesson: the container’s writable layer is disposable by design. Docker gives you three ways to attach storage to containers — volumes, bind mounts, and tmpfs mounts — and they differ in who manages them, where they live, and how fast they are.

Why containers need external storage

A container’s filesystem is tied to its lifecycle. Remove the container, and its writable layer goes with it. Even without removal, image updates create fresh containers. Any data that must outlast a single container — databases, uploads, logs — has to live somewhere else.

Named volumes: Docker-managed storage

Volumes are the preferred mechanism for persisting container data, according to the Docker volumes documentation. Docker creates and manages them under /var/lib/docker/volumes/ (on Linux), completely decoupled from any host directory structure.

# Create and use a named volume
docker volume create app-data

docker run -d --name db \
  --mount source=app-data,target=/var/lib/postgresql/data \
  postgres:16

Volumes are the right default because they:

  • Survive container removal — data persists until you explicitly docker volume rm.
  • Are portable across hosts and OSes — no assumptions about host paths, works on Linux and Windows containers.
  • Can be shared safely by multiple containers simultaneously.
  • Support backup/migration workflows — docker run --volumes-from, drivers for NFS/S3, and simple tar-based backups.
  • Perform well — writes go directly to the host filesystem, bypassing the union filesystem/storage-driver overhead of the container’s writable layer.
  • Can be pre-populated — mounting an empty volume copies the image’s existing content in by default (disable with volume-nocopy).

Anonymous volumes (no name, random ID) also persist after container removal — unless you used docker run --rm, which cleans them up. Named volumes are easier to reason about, so prefer them.

Volumes are not the tool when you need to edit files from the host with your own editors — Docker manages their internals for you.

Bind mounts: host directory sharing

A bind mount maps any host path directly into the container:

docker run -d --name dev-web \
  --mount type=bind,src=$(pwd)/src,dst=/usr/share/nginx/html \
  nginx:alpine

Bind mounts couple the container to the host’s directory structure and OS. Use them when:

  • Development hot-reloading — mount your source tree and let the container watch for changes.
  • Sharing host config — nginx.conf, certificate directories, a Docker socket (with extreme care).
  • You need host-side access — files must be readable/writable by processes outside Docker.

The coupling is also the risk: paths differ per machine, files created inside the container can end up owned by unexpected UIDs on the host, and the app works on your laptop but not your CI runner. For application data in production, reach for volumes instead.

Between the two, --mount is the recommended syntax — it’s explicit, and it’s the only way to use advanced options like volume-subpath or volume drivers.

tmpfs mounts: memory-backed, ephemeral

A tmpfs mount lives only in the host’s RAM and vanishes when the container stops:

docker run -d --name api \
  --tmpfs /run:rw,noexec,nosuid,size=65536k \
  my-api:latest

Use tmpfs for data that should never touch disk:

  • Secrets and credentials processed at runtime.
  • Scratch space where persistence would be a liability (session stores, caches).
  • Performance-sensitive temporary files — RAM is faster than any disk, and you avoid bloating the container’s writable layer.

The trade-off is obvious: the data is gone on restart, and it counts against the container’s memory limits.

Choosing between them

Named volumeBind mounttmpfs
Managed byDockerYouDocker (RAM)
Host location/var/lib/docker/volumesAny path you chooseHost memory
Persists after container removalYesYes (it’s the host’s)No
Portable across OSesYesNoYes
Editable from hostIndirectlyDirectlyNo
PerformanceHighHighHighest
Best forApp data, databases, uploadsDev source, host configSecrets, caches, scratch

Rule of thumb: application data → volume; things that are part of your machine or dev environment → bind mount; things that must never be written to disk → tmpfs.

Putting it together with Compose

services:
  db:
    image: postgres:16
    volumes:
      - db-data:/var/lib/postgresql/data   # named volume
  web:
    image: nginx:alpine
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro   # bind mount, read-only
volumes:
  db-data:

Mounting the database data directory as a named volume means docker compose down && docker compose up — even with --volumes carelessness aside — doesn’t destroy your data when containers are recreated.

The silent killer: the writable layer

One more path exists: writing into the container’s own filesystem without any mount. It works until the container is replaced, and it inflates the image layer. Docker’s docs call it out explicitly — volumes don’t increase container size and avoid the storage-driver abstraction that slows writes. If your process writes to a location, give it a mount: volume for durability, tmpfs for secrecy.

Further reading