sehaj/learn
Docker/04 Data

04.03 · with code · 11 min

Bind mounts

Mount a folder from your machine into a container: live-edit code, inject config, and avoid the three traps (hidden files, missing paths, root-owned output).

The core idea

A bind mount maps a specific folder (or file) on your machine to a path in the container. It isn't a copy: both sides see the same files, and a change on one side shows up on the other immediately. That makes it ideal for development and for handing a container config files. It's a poor fit for production data, which belongs in volumes.

Live editing

❯terminal
$ mkdir site && echo '<h1>v1</h1>' > site/index.html
$ docker run -d --name live -p 8086:80 -v "$PWD/site":/usr/share/nginx/html:ro nginx:alpine
$ curl localhost:8086
<h1>v1</h1>
$ echo '<h1>v2, edited on the host</h1>' > site/index.html
$ curl localhost:8086
<h1>v2, edited on the host</h1>

No rebuild, no restart. :ro stops the container from writing back:

❯terminal
$ docker exec live sh -c 'echo x > /usr/share/nginx/html/new.html'
sh: can't create /usr/share/nginx/html/new.html: Read-only file system

The source must be an absolute path ("$PWD/site", not site). A bare name like site would be read as a volume name instead.

Trap 1: the mount hides what was there

A bind mount replaces the whole directory. Whatever the image had at that path becomes invisible:

❯terminal
$ docker run --rm -v "$PWD/site":/usr/share/nginx/html nginx:alpine ls /usr/share/nginx/html
index.html

nginx's own 50x.html is gone; only your folder's contents show. The classic case is mounting a Node project over /app for development: the image's node_modules disappears behind your folder, which has none (or has packages built for macOS):

❯terminal
$ docker run --rm notes-dev ls /app
dist node_modules package-lock.json package.json src tsconfig.json
$ docker run --rm -v "$PWD":/app notes-dev ls /app
package-lock.json package.json src tsconfig.json
$ docker run --rm -v "$PWD":/app -v /app/node_modules notes-dev ls /app
node_modules package-lock.json package.json src tsconfig.json

(Output joined onto one line per command.) The fix in the last line adds an anonymous volume at /app/node_modules. The more specific mount wins, so the image's node_modules shows through again (it's an empty volume, so it gets pre-filled; see 04.02 Volumes). Docker also creates an empty node_modules folder in your project as the mount point.

Trap 2: missing paths

The two syntaxes disagree about a host path that doesn't exist:

❯terminal
$ docker run --rm -v "$PWD/does-not-exist":/data alpine true
$ ls -d does-not-exist
does-not-exist
$ docker run --rm --mount type=bind,src="$PWD/does-not-exist",dst=/data alpine true
docker: Error response from daemon: invalid mount config for type "bind": bind source path does not exist: ...

-v quietly creates an empty directory, which is how a typo becomes "my config file is empty". --mount refuses. In scripts, prefer --mount.

Trap 3: who owns the files

A container running as root that writes into a bind mount creates files owned by UID 0. On a Linux host, those files are then owned by root on your machine too, and you need sudo to delete them. Run the container as your own user to avoid it:

linux
docker run --rm -u "$(id -u):$(id -g)" -v "$PWD":/work -w /work node:22-slim npm run build

Docker Desktop on macOS and Windows hides this: its file sharing maps ownership to your user, so the same file shows as yours on the Mac. Code that works on your laptop can still leave root-owned files on a Linux CI runner.

Predict first

You start a dev container with -v ./src:/app/src, but there's a typo and the folder is actually ./scr. The app starts and can't find any source files. Why?

Commit to an answer before you read on.
CheckpointA live-reload dev setup

Serve a folder with nginx as above and edit it live. Then reproduce trap 1 with the notes API's build stage (docker build --target build -t notes-dev .) and fix it with the extra -v /app/node_modules. Finally try the --mount version with a typo and read the error.

STUCK? ▸

Changes don't show up: some editors replace files instead of writing to them, and a bind-mounted single file keeps pointing at the old one. Mount the folder, not the file.

Recap

Interview questions

JuniorfreqMust-knowConcept

When would you use a bind mount instead of a volume?

MidfreqGood to knowDebugging

Your dev container mounts the project into /app and now the app can't find its dependencies. What happened?

Report a mistakeEdit this page

Fundamentals you'll use

☰ Data04.03 · 24/44next →