iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

Bind Mounts

A bind mount maps a directory on the host into a container. Use them when you want changes on the host to appear instantly in the container — the canonical case is mounting your source tree during local development. They are NOT a portable persistence mechanism; for production data use named volumes instead.

Bind mounts for dev loop, plus the gotchas

EXAMPLE
# 1) Mount the current directory into the container's /app
docker run --rm -it \
  -v "$(pwd)":/app \
  -w /app \
  node:20-alpine \
  npm run dev

# 2) Read-only bind mount — useful for configs you do not want the container to mutate
docker run --rm -it \
  -v /etc/nginx/conf.d:/etc/nginx/conf.d:ro \
  nginx:1.27

# 3) The --mount syntax is more explicit and harder to typo
docker run --rm -it \
  --mount type=bind,source="$(pwd)",target=/app,readonly \
  node:20-alpine node /app/index.js

# 4) docker-compose: bind mount the code AND mask node_modules with a volume
# This is the classic 'live reload but keep container-installed deps' pattern.
# docker-compose.yml
# services:
#   web:
#     image: node:20-alpine
#     working_dir: /app
#     command: npm run dev
#     volumes:
#       - .:/app                       # bind mount: host code -> container
#       - /app/node_modules            # anonymous volume MASKS host's node_modules
#     ports:
#       - 3000:3000

# 5) Gotchas you will hit

# a) Permissions: files created in the container land on the host owned by
#    the container's user. On Linux this is often UID 0 (root) if the
#    container did not switch user. Fix by running the container as your UID:
docker run --rm -u "$(id -u):$(id -g)" -v "$(pwd)":/app node:20-alpine \
  npm install

# b) Performance: bind mounts on macOS/Windows go through a network FS layer.
#    Mount only the paths you need. For node_modules, prefer a named volume.

# c) SELinux (RHEL/Fedora) blocks bind mounts by default. Append :z (shared) or :Z (private):
docker run -v "$(pwd)":/app:z fedora:40 ls /app

# d) Inotify limits: large source trees can exhaust inotify watchers, breaking
#    file-watch reloaders. Raise the limit on the host:
sudo sysctl fs.inotify.max_user_watches=524288

# 6) Inspect bind mounts on a running container
docker inspect web --format '{{ range .Mounts }}{{ .Type }} {{ .Source }} -> {{ .Destination }}{{ println }}{{ end }}'

# 7) Production tip: DO NOT bind-mount production code. Ship a baked image.
#    Bind mounts couple the runtime to the host filesystem, which is fine in dev
#    but defeats the point of immutable images at deploy time.

Why it matters

The compose pattern - bind-mount the code AND declare an anonymous volume at the path of node_modules / vendor / target - is the trick that makes hot-reload Just Work without dragging the hosts dependencies into the container. Without the anonymous volume, an `npm install` on macOS clobbers the much faster Linux node_modules the container built at image time.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
# Mount a host path into the container — perfect for dev hot-reload.
docker run -v $(pwd)/src:/app/src node:20
Try it Yourself »

Discussion

Loading…