Lesson 3.3: Bind Mounts
Welcome to Lesson 3.3! You've already mastered volumes for persistent, Docker-managed storage. Now we'll explore bind mounts – a different approach that gives you direct access to the host filesystem. Bind mounts are incredibly useful for development, injecting configuration, and sharing source code between the host and containers. By the end of this lesson, you'll understand when to use bind mounts, how they differ from volumes, and how to use them effectively.
Learning Objectives
TIP
By the end of this lesson, you will be able to:
- Explain what a bind mount is and how it differs from a volume.
- Mount a host directory into a container using
-vor--mount. - Use bind mounts for development (live code reload) and configuration management.
- Understand the implications of host filesystem permissions on bind mounts.
- List the pros and cons of bind mounts compared to volumes.
- Recognize security considerations when using bind mounts.
1. What Are Bind Mounts?
A bind mount is a mapping between a host directory (or file) and a location inside a container. Unlike volumes, which are managed by Docker, bind mounts give you full control over the host path. The container sees the exact same files as the host, and changes made on either side are immediately reflected on the other.
Bind mounts have been available since the early days of Docker and are still heavily used, especially in development environments.
Key characteristics:
- The host path is absolute (e.g.,
/home/user/projecton Linux,C:\Users\user\projecton Windows). - If the host directory doesn't exist, Docker creates it automatically (with some caveats on different OSes).
- Bind mounts bypass the Docker storage driver; they are direct filesystem mounts.
- They provide no additional features like snapshots or backup commands (those are up to you).
2. Bind Mounts vs. Volumes
| Feature | Bind Mounts | Volumes |
|---|---|---|
| Management | Managed by user (host paths) | Managed by Docker (docker volume commands) |
| Portability | Host-specific paths; not portable across machines | Portable; can be named and shared |
| Performance | Native host filesystem performance | Native host filesystem performance |
| Backup/Restore | Use host tools (tar, rsync) | Docker-specific commands (via container) |
| Use cases | Development, injecting config, sharing source | Production, databases, any stateful service |
| Host dependency | Directly depends on host directory structure | Independent of host directory structure |
| Permissions | Follow host permissions | Docker manages ownership (root by default) |
| Security | Can expose sensitive host directories | More isolated (within Docker's control) |
When to use bind mounts:
- Development: Mount source code into a container to see changes immediately without rebuilding.
- Configuration injection: Override config files in a container with host-side versions.
- Sharing host tools: Access host binaries or Unix sockets (e.g., Docker socket).
- Data that must be accessed directly on host: When you need to manipulate files using host tools.
When to use volumes:
- Production data (databases, application state).
- Data that should be portable across different hosts.
- When you need backup/restore features via Docker CLI.
3. Mounting a Bind Mount
You can mount a bind mount using either -v or the more explicit --mount flag. The syntax differs slightly.
3.1. Using -v (Volume flag)
Syntax:
-v /host/path:/container/path[:options]- Host path must be absolute.
- If the host path contains spaces, wrap it in quotes.
- Options:
ro(read-only),rw(read-write, default),z/Zfor SELinux.
Example:
docker run -v /home/user/myapp:/app -w /app node:18 npm start3.2. Using --mount
The --mount flag is more explicit and preferred in scripts or when you need additional options.
Syntax:
--mount type=bind,source=/host/path,target=/container/path[,readonly]type=bindindicates a bind mount.sourceis the absolute host path.targetis the container mount point.readonly(optional) mounts as read-only.
Example:
docker run --mount type=bind,source=/home/user/myapp,target=/app node:18 npm start3.3. Read-Only Bind Mounts
To prevent the container from writing to the mounted directory, add readonly (with --mount) or :ro (with -v).
docker run --mount type=bind,source=/home/user/config,target=/etc/app/config,readonly myapp4. Development Workflow with Bind Mounts
Bind mounts are a developer's best friend. They allow you to edit source code on your host and immediately see changes inside the container without rebuilding the image.
4.1. Live Reload Example (Node.js)
Create a simple Node.js app on your host:
bashmkdir mynodeapp cd mynodeapp echo 'console.log("Hello from Node!");' > index.jsRun a Node container with a bind mount to the current directory, and run the script:
bashdocker run --rm -v $(pwd):/app -w /app node:18 node index.jsOutput:
Hello from Node!Modify
index.json your host (e.g., change the message), and re-run the same command. The change is picked up immediately because the container sees the host filesystem.
4.2. Hot Reload with Nodemon (Node.js)
For a more realistic development server, use a tool like nodemon to restart the server on file changes.
docker run --rm -v $(pwd):/app -w /app node:18 sh -c "npm install && npx nodemon index.js"Now any change to index.js triggers a restart automatically.
5. Configuration Injection with Bind Mounts
Bind mounts are perfect for overriding configuration files without modifying the image.
Example: Override Nginx default page.
- Create a custom
index.htmlon your host:bashecho "<h1>Custom Nginx Page</h1>" > custom-index.html - Run Nginx and mount the custom file over the default
index.html:bashdocker run -d --name nginx-custom -p 8080:80 -v $(pwd)/custom-index.html:/usr/share/nginx/html/index.html:ro nginx - Visit
http://localhost:8080– you'll see your custom page.
6. Permissions and Ownership
When you bind mount a host directory, the files inside retain their original ownership and permissions. Inside the container, the user (often root or a specific application user) must have appropriate permissions to read/write those files.
6.1. User Mismatch
If the container runs as a non-root user (e.g., node user in the official Node image) and the mounted files are owned by your host user, the container may not be able to write to them. This can cause permission errors.
Solutions:
- Run the container as root (not recommended for production).
- Adjust file permissions on the host to allow the container user to write (e.g.,
chmod 777– but be careful). - Use user namespace remapping (advanced).
- In development, you can often ignore this if you only need read access, or you can create a Dockerfile that adds a user with the same UID as your host user.
6.2. Example Permission Issue
# Create a file owned by your host user
echo "data" > data.txt
# Run a container as node user (UID 1000) trying to write to the mounted file
docker run --rm -v $(pwd):/data node:18 sh -c "echo new >> /data/data.txt"This may fail because the node user (UID 1000) may not have write permission. Check the actual UID in the image with docker run --rm node:18 id.
7. Security Considerations
- Avoid mounting sensitive host directories (e.g.,
/etc,/var/run/docker.sock) unless absolutely necessary. Doing so can give containers root-like access to your host. - Use
readonlymounts when the container does not need to write to the mounted files. - Be cautious with relative paths in
-v; Docker may interpret them as volume names instead of bind mounts if they don't start with/or./. Always use absolute paths for bind mounts.
8. Bind Mounts on Different Operating Systems
- Linux: Works natively with absolute paths (e.g.,
/home/user/project). - macOS / Windows (Docker Desktop): Bind mounts are available, but the host path is relative to the Docker Desktop VM. For example,
/Users/username/projecton macOS,C:\Users\username\projecton Windows. Docker Desktop automatically shares these directories with the VM. - Windows containers: Bind mounts also work but follow Windows path conventions (e.g.,
C:\data).
Hands-On Tasks
Task 1: Basic Bind Mount
- Create a directory
~/docker-bindand create a filetest.txtinside it with some content. - Run an Alpine container that mounts that directory to
/mntand displays the file:bashdocker run --rm -v ~/docker-bind:/mnt alpine cat /mnt/test.txt - Modify the file on your host and run the command again. The change is reflected.
Task 2: Live Development with Python
- Create a directory
flask-app. - Inside, create a file
app.py:pythonfrom flask import Flask app = Flask(__name__) @app.route('/') def hello(): return "Hello from Flask!" if __name__ == '__main__': app.run(host='0.0.0.0', port=5000) - Run a container with Flask installed, mounting the directory, and exposing port 5000:bash
docker run --rm -p 5000:5000 -v $(pwd):/app -w /app python:3.11-slim sh -c "pip install flask && python app.py" - Access
http://localhost:5000. Modifyapp.py(e.g., change the message) and refresh. Flask's debug mode would auto-reload if enabled; here you'll need to restart the container to see changes (unless you use a development server with hot reload).
Task 3: Inject Configuration into Nginx
- Create a custom
nginx.confon your host that changes the default welcome page text. - Run an Nginx container with a bind mount for the config file (and also a bind mount for the default HTML if you want).
- Verify the configuration change.
Task 4: Permission Experiment
- Create a directory
sharedand a filedata.txtowned by your user. - Run a container as a non-root user (e.g.,
--user 1000:1000) and try to write to the mounted file:bashdocker run --rm -v $(pwd)/shared:/data --user 1000:1000 alpine sh -c "echo test >> /data/data.txt" - Check the exit code. If it fails, adjust permissions (
chmod 777 shared/data.txt) and try again.
Task 5: Read-Only Bind Mount
- Run a container with a read-only bind mount to your home directory (or a test directory).
- Attempt to write a file inside the mount. It should fail with a read-only filesystem error.
Task 6: Sharing the Docker Socket (Advanced)
- Mount
/var/run/docker.sockinto a container to allow the container to control the host Docker daemon. This is powerful but risky.bashdocker run -v /var/run/docker.sock:/var/run/docker.sock docker:latest docker ps - This gives the container administrative access to your Docker. Only do this in trusted environments.
Summary
Key Takeaways
- Bind mounts map a host directory or file directly into a container.
- They are ideal for development (live reload), configuration injection, and sharing host resources.
- Use absolute paths for bind mounts; relative paths may be misinterpreted as named volumes.
- Bind mounts bypass Docker's storage driver and rely on host filesystem permissions.
- For production stateful workloads, volumes are preferred due to better management and portability.
- Security: avoid mounting sensitive host directories; use read-only mounts when possible.
Check Your Understanding
- What is the main difference between a bind mount and a volume?
- Write a
docker runcommand to mount the host directory/home/user/projectto/appin the container, using--mount. - When would you choose a bind mount over a volume?
- What permission issue might arise when mounting a host directory into a container that runs as a non-root user?
- How can you mount a host file as read-only inside a container?
- Why is mounting
/var/run/docker.sockconsidered a security risk?
Click to see answers
- Bind mounts map a specific host directory into the container and are managed by the user. Volumes are managed by Docker with their own CLI commands and stored in Docker's managed directories.
docker run --mount type=bind,source=/home/user/project,target=/app myimage- During development (for live code reload), when injecting configuration files, or when you need access to host tools or sockets.
- If the container runs as a non-root user whose UID doesn't match the host file owner, the container user may not have permission to read or write the mounted files.
- Add
readonlyto--mountor:roto-v. - It gives the container access to the Docker daemon on the host, effectively giving it root-level control over Docker, including the ability to start privileged containers or access the host filesystem.
Additional Resources
- Docker bind mounts documentation
- Manage data in Docker: bind mounts vs volumes
- Docker run reference: volume and bind mount options
- User namespace remapping for better isolation
Next Up
In the next lesson, we'll cover tmpfs mounts – an in-memory storage option for temporary, non-persistent data. See you there!