Bind Mounts
What is a bind mount?
Section titled “What is a bind mount?”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 mountContainer filesystem: /app/srcAny 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.
Syntax
Section titled “Syntax”Short -v syntax:
docker run -v /host/path:/container/path myimageLong --mount syntax (recommended for scripts — no ambiguity):
docker run \ --mount type=bind,source=/host/path,target=/container/path \ myimageUsing $(pwd) for the current directory
Section titled “Using $(pwd) for the current directory”In development you almost always want to mount the current working directory:
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.
A practical development workflow
Section titled “A practical development workflow”# Mount your source into the container at /app# The -w flag sets the working directory inside the containerdocker run -d --name dev-server \ -v $(pwd)/src:/app/src \ -p 3000:3000 \ node:24-alpine \ node /app/src/server.jsNow 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.
Read-only bind mounts
Section titled “Read-only bind mounts”Add :ro to make the mount read-only from the container’s perspective. The container can read the files but cannot write to them:
docker run --rm \ -v $(pwd)/config:/etc/myapp/config:ro \ myimageWith --mount syntax:
docker run --rm \ --mount type=bind,source=$(pwd)/config,target=/etc/myapp/config,readonly \ myimageThis is useful for injecting configuration files without the container accidentally overwriting them.
Hands-on practice
Section titled “Hands-on practice”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/bindtestTrade-offs
Section titled “Trade-offs”| Option | Benefit | Cost |
|---|---|---|
| Bind mount | Edit a file on the host and see it in the container instantly, great for live reload during development | Tied to that host’s directory layout; moving to another host needs the same path |
| Named volume | Docker manages the storage for you, portable across hosts | Have to go into the container to see the files — cannot open them directly with an editor on the host |
Common mistakes
Section titled “Common mistakes”- 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_modulesdirectory 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/datafor 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).