Maintenance

Site is under maintenance — quizzes are still available.

Go to quizzes

Use Bind Mounts for Dev

Learn to use bind mounts for development with Docker: mount host directories into containers for real-time code syncing, no rebuilds.

Focus: use bind mounts for development

Sponsored

You've built a Docker image, started a container, and maybe even written a Compose file. But every time you change one line of JavaScript, Python, or HTML, you rebuild the image and restart the container. In minutes, that friction kills your flow. Bind mounts solve that by letting your container see and edit files directly from your host — changes appear instantly, no rebuilds required.

The problem this lesson solves

Every Docker beginner hits the same wall: you edit your source code, save the file, and the running container still shows the old version. So you run docker build again, wait for layers to cache, then docker run or restart Compose. That loop eats seconds, then minutes, then hours over a project. During development, you don't need isolation — you need iteration speed.

Bind mounts fix this by breaking the wall between your host filesystem and the container filesystem. Instead of copying your code into the image (like COPY does in a Dockerfile), you tell Docker: "use this folder on my laptop as the container's folder." Any edit you make in your editor hits the container immediately.

Core concept / mental model

Think of a bind mount as a live portal between a directory on your host machine and a directory inside your container. What you change on one side instantly appears on the other. There is no copy step, no build step — just a direct mapping managed by the Docker engine.

Pro tip: Docker has two types of mounts — bind mounts and volumes. Volumes are Docker-managed and great for persistent data (databases). Bind mounts are host-path-managed and perfect for live code editing.

Key definitions

  • Host path: the absolute or relative directory on your laptop/desktop (e.g., /Users/you/project or ./app).
  • Container path: the directory inside the container where those files appear (e.g., /usr/src/app).
  • Mount flag: --mount type=bind,source=<host-path>,target=<container-path> or the shorthand -v <host-path>:<container-path>.

Why use bind mounts for development?

Without bind mount With bind mount
Edit code → rebuild image → restart container → see change Edit code → see change instantly
Image contains stale snapshot of code Container always sees latest files
Slow iteration on UI, CSS, or config Real-time feedback loop
Hard to test small tweaks Tweak, save, refresh — done

How it works step by step

  1. Start a container with a bind mount. When you run docker run, add the --mount or -v flag pointing to your project directory.
  2. Docker binds the host directory into the container at the container path you specify. This is a kernel-level operation — no copying, no syncing.
  3. Your container process reads and writes as if those files were always there. The same inode and file handles are shared.
  4. You edit files on your host. Your IDE’s save triggers a filesystem event that both host and container see.
  5. The container’s running application (web server, script, watcher) detects the changes and reloads — or you manually restart the process inside the container.

Important: If the container writes to a bind-mounted directory, those changes appear on your host too. That’s great for log files or generated output, but be careful — you don’t want a container accidentally deleting your source.

Running with -v (shorthand)

docker run -p 3000:3000 -v $(pwd):/app node:18-alpine node /app/server.js

Running with --mount (verbose, recommended for clarity)

docker run -p 3000:3000 --mount type=bind,source="$(pwd)",target=/app node:18-alpine node /app/server.js

Both commands do the same thing: mount the current directory ($(pwd)) into /app inside the container. Changes to server.js are live.

Hands-on walkthrough

Let's build a tiny Node.js app to see the use bind mounts for development flow in action.

1. Create a simple app

Create app.js on your host:

const http = require('http');
const server = http.createServer((req, res) => {
  res.end('Hello from Docker!\n');
});
server.listen(3000, () => console.log('Running on port 3000'));

2. Run with a bind mount

docker run -d -p 3000:3000 --name dev-test -v $(pwd):/app node:18-alpine node /app/app.js

Test it: curl http://localhost:3000 → shows Hello from Docker!.

3. Edit the file live

Change 'Hello from Docker!' to 'Hello from bind mount!' in app.js on your host. Save the file.

4. See the change — no rebuild

curl http://localhost:3000

Hello from bind mount!

The running Node.js server picked up the new file because the bind mount reflects the host file immediately.

Cleanup

docker stop dev-test
docker rm dev-test

Compare options / when to choose what

Mount type Description Best for
Bind mount Maps a host path directly Development — live code editing, config files
Volume Docker-managed storage Databases, persistent data you don't want on host
tmpfs mount In-memory, temporary Secrets that must not hit disk

When to use bind mounts for development

  • Framework apps (Node, React, Flask, Django) — edit, save, refresh.
  • Config hot-reloading — mount nginx.conf, restart nginx in the container.
  • Log collection — mount a host directory for container logs.

When not to use bind mounts

  • Production — never mount your development code into a production container. Use COPY in a Dockerfile for reproducible builds.
  • Windows/macOS performance — bind-mounted directories on non-Linux hosts can be slower due to filesystem translation. Use volumes for high-I/O scenarios.

Troubleshooting & edge cases

1. The container says "file not found"

Cause: The mount path inside the container doesn't exist, or you used an absolute host path that doesn't match.

Fix: Docker creates the destination directory if it doesn't exist, but only the last component. Ensure source is correct: -v /Users/you/project:/app works; /app is created inside.

Pro tip: Always use $(pwd) or an absolute path for the source. Relative paths like -v ./app:/app work from the shell’s current directory — but be explicit in scripts.

2. Changes not showing inside the container

Cause: The application pre-caches files or uses an older node process.

Fix: Use a file-watcher inside the container (like nodemon) or restart the process manually:

docker exec dev-test kill -HUP 1   # SIGHUP to PID 1, if your app supports it

3. Permission denied errors

Cause: The container process runs as a non-root user but the mounted files are owned by your host user (UID 1000).

Fix: Remap UIDs in the Dockerfile or run the container with --user matching your host UID:

docker run -u $(id -u):$(id -g) -v $(pwd):/app node:18-alpine node /app/app.js

4. Empty directory on container side

Cause: A volume or named mount shadows the bind mount path.

Fix: Check docker inspect <container> — look at Mounts list. Remove conflicting volumes.

What you learned & what's next

You now understand use bind mounts for development — how to map a host directory into a container for real-time file syncing without rebuilds. You can apply it to any framework: Node, Python, Go, React. The core idea is simple: bind mounts remove the build step from the edit-run cycle.

Key learning objectives covered: - Explain the core idea behind bind mounts for development. - Complete a practical exercise with a live-edited Node.js app.

Next lesson: Use named volumes for persistent data — you'll learn how volumes differ from bind mounts and why databases belong in Docker volumes, not bind mounts.

Practice recap

Try this: Create a simple Python Flask app with a single route. Run it in a container with a bind mount and the Flask reloader enabled. Edit the route's response — you should see the change on the next HTTP request without restarting the container. Then repeat with Docker Compose using the volumes key instead of -v.

Common mistakes

  • Mounting a subdirectory (e.g., ./src) but the container expects the root of the project — results in missing files like package.json or requirements.txt.
  • Using COPY in Dockerfile and also running with a bind mount — the COPY files are overwritten by the mount, leading to confusion about which files are used.
  • Forgetting to stop the container before editing — the container is live but the app may cache old content. Use a file watcher or restart process.
  • Running on Windows with Git Bash or PowerShell and using $(pwd) which includes backslashes or spaces — quote the path or use %cd% in CMD.

Variations

  1. Use docker-compose.yml volumes key: volumes: - ./app:/app — same bind mount behavior, declarative syntax.
  2. Use read-only bind mounts: --mount type=bind,source=$(pwd),target=/app,readonly — prevents accidental writes from the container to host.
  3. Mount multiple source directories: -v ./src:/app/src -v ./config:/app/config — useful for monorepos or split configuration.

Real-world use cases

  • Frontend dev (React/Vue): mount project directory, run npm run dev inside container — HMR works because files are live.
  • Django/Flask app: mount project root, run development server with --reload — Python picks up changes immediately.
  • API development with config: mount nginx.conf and static assets, edit configs, reload nginx via docker exec — no rebuild per tweak.

Key takeaways

  • Bind mounts map a host directory into a container — changes are immediate, no rebuilds needed.
  • Use --mount type=bind,source=...,target=... for clarity; -v shorthand works but can be ambiguous.
  • Bind mounts are for development only — use COPY in Dockerfile for reproducible production images.
  • File watchers inside the container (nodemon, webpack-dev-server) still work because the kernel sees the host files.
  • Permission issues are common — match container UID with host UID via --user flag.
  • Always clean up: docker rm after stopping dev containers to prevent orphans.

Sponsored

Sponsored

Discussion

Questions, corrections, and tips help everyone reading this page.

0 comments

Add a comment

Shown publicly with your comment.

Be constructive · max 4,000 characters

No comments yet — start the thread.

Related tutorials, quizzes, and articles for this topic.