Skip to content

Bind Mounts

A bind mount maps a specific path on your host machine directly into a container. Unlike a named volume, you choose exactly where the data lives on the host — Docker does not manage the path.

Host filesystem: /home/user/myapp/src
↕ bind mount
Container filesystem: /app/src

Any change made on the host side is immediately visible inside the container, and vice versa. This makes bind mounts ideal for development: your editor modifies files on the host, and the running process inside the container sees the updates instantly — no rebuild required.

Short -v syntax:

Terminal window
docker run -v /host/path:/container/path myimage

Long --mount syntax (recommended for scripts — no ambiguity):

Terminal window
docker run \
--mount type=bind,source=/host/path,target=/container/path \
myimage

In development you almost always want to mount the current working directory:

Terminal window
docker run --rm -v $(pwd):/app -w /app node:24-alpine node server.js

$(pwd) expands to the absolute host path at runtime. This is the standard pattern for running code without building a new image on every change.

Terminal window
# Mount your source into the container at /app
# The -w flag sets the working directory inside the container
docker run -d --name dev-server \
-v $(pwd)/src:/app/src \
-p 3000:3000 \
node:24-alpine \
node /app/src/server.js

Now edit src/server.js on your host. If the process inside the container watches the filesystem (e.g., with nodemon), it restarts automatically. No image rebuild is needed for every code change.

Add :ro to make the mount read-only from the container’s perspective. The container can read the files but cannot write to them:

Terminal window
docker run --rm \
-v $(pwd)/config:/etc/myapp/config:ro \
myimage

With --mount syntax:

Terminal window
docker run --rm \
--mount type=bind,source=$(pwd)/config,target=/etc/myapp/config,readonly \
myimage

This is useful for injecting configuration files without the container accidentally overwriting them.

The snippet below creates a file on the host (simulated here with echo in a setup step), mounts the directory into a container, and reads the file from inside the container — demonstrating live file sharing.

# 1. Create a directory with a file to share
mkdir -p /tmp/bindtest && echo 'shared from host' > /tmp/bindtest/hello.txt

# 2. Mount it into a container and read the file
docker run --rm -v /tmp/bindtest:/data alpine cat /data/hello.txt

# 3. Write from inside the container back to the host
docker run --rm -v /tmp/bindtest:/data alpine sh -c "echo 'written by container' > /data/from-container.txt"

# 4. Check the file now exists on the host path
ls -la /tmp/bindtest/
cat /tmp/bindtest/from-container.txt

# 5. Clean up
rm -rf /tmp/bindtest
OptionBenefitCost
Bind mountEdit a file on the host and see it in the container instantly, great for live reload during developmentTied to that host’s directory layout; moving to another host needs the same path
Named volumeDocker manages the storage for you, portable across hostsHave to go into the container to see the files — cannot open them directly with an editor on the host
  • Using a bind mount to store production database data instead of a named volume, so deploying to another host with a different path loses the data.
  • Forgetting that a bind mount carries the container’s file permissions onto the host, so files on the host can change ownership unexpectedly.
  • Mounting an entire node_modules directory from the host into the container, causing dependencies built on the host’s OS to conflict with what the container needs.

💡 Real-world example

The official Postgres image recommends a named volume at /var/lib/postgresql (Postgres 18+; /var/lib/postgresql/data for 17 and earlier), and the official MySQL image recommends /var/lib/mysql — never a bind mount, because production needs the portability a bind mount cannot give. A bind mount is only for source code during development (hub.docker.com/_/postgres, hub.docker.com/_/mysql).

What is the key difference between a bind mount and a named volume?
Which shell expression expands to the current working directory in a docker run command?
What does adding :ro to a bind mount do?
Why are bind mounts preferred for local development but not recommended for production?