Bind mounts and tmpfs

Docker · Practical · 10 min

Bind mounts give the edit-and-reload loop for development; knowing their pitfalls saves hours.

A bind mount maps an existing path on the host directly into the container. Unlike a volume, Docker doesn't manage it; the container sees your real files, and changes go both ways instantly.

docker run -v "$PWD":/app -w /app node:22 npm run dev

That is the classic development setup: your editor changes files on the host, the dev server in the container reloads. The --mount form is --mount type=bind,src="$PWD",dst=/app; unlike -v, it errors if the source path doesn't exist instead of creating an empty directory, which catches typos.

Key behaviours and gotchas:

  • A bind mount hides whatever the image had at that path. Mounting your project over /app also hides the node_modules installed in the image. A common fix is an extra anonymous volume for that subfolder: -v /app/node_modules.
  • Permissions: files are owned by numeric UIDs. A container running as root creates root-owned files in your project; a non-root container may be unable to write host files. Run with --user "$(id -u):$(id -g)" on Linux when needed.
  • Performance on Docker Desktop is lower than on native Linux, because files cross the VM boundary; synchronized file shares or volumes help for heavy I/O.
  • Append :ro (or readonly with --mount) for config files the container should only read.
  • Bind mounts tie a container to the host's directory layout, so use them for development and for specific host files, and use volumes for application data.

A tmpfs mount (--tmpfs /tmp) lives only in memory and vanishes when the container stops. Use it for scratch files and secrets that must never touch disk, and pair it with --read-only root filesystems.

Example

# Live-reload dev server, keeping the image's node_modules
docker run --rm -it -p 5173:5173 \
  -v "$PWD":/app -v /app/node_modules -w /app \
  node:22 npm run dev -- --host

# Read-only config file from the host
docker run -d --name proxy \
  --mount type=bind,src="$PWD"/nginx.conf,dst=/etc/nginx/nginx.conf,readonly \
  -p 8080:80 nginx:1.27

# Avoid root-owned files in your project (Linux)
docker run --rm -v "$PWD":/w -w /w --user "$(id -u):$(id -g)" alpine touch out.txt

# In-memory scratch space on a read-only container
docker run --rm --read-only --tmpfs /tmp alpine sh -c 'echo hi > /tmp/x && cat /tmp/x'

Remember

  • Bind mounts expose a host path directly; changes are live both ways
  • They hide the image's content at the mount point
  • Watch UID ownership and Docker Desktop I/O performance
  • tmpfs mounts are in-memory and disappear on stop

Watch out for

  • Mounting the project over /app and wondering where node_modules went.

Check yourself

  1. You bind-mount your project to /app, but the image installed dependencies in /app/node_modules. What does the container see?
  2. Which mount type should hold a production database's data?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.docker.com/engine/storage/bind-mounts/

Builds on

Unlocks

Nothing builds on this yet.