DEV Community

Jhoan Sebastian Rojas Holguin
Jhoan Sebastian Rojas Holguin

Posted on AI-assisted

Docker Volumes vs Bind Mounts (Step-by-Step Guide) 🐳

Learn Docker data persistence with a practical 5-minute lab comparing Docker Volumes and Bind Mounts.

Docker Data Persistence: Volumes vs Bind Mounts πŸ§ͺ

By default, Docker containers are ephemeral. If a container stops or gets deleted, any data created inside it vanishes forever.

To prevent data loss, Docker provides two primary mechanisms for data persistence: Docker Volumes and Bind Mounts.

In this practical guide, we will walk through a 5-minute hands-on lab so you can test both approaches directly on your machine.


πŸ“Š Quick Comparison Matrix

Feature Docker Volumes (VolΓΊmenes) Bind Mounts
Managed By Docker Engine (/var/lib/docker/volumes/) User / Host OS
Path on Host Managed automatically by Docker Explicit path (e.g., /home/user/app)
Best For Production, Databases, Persistent Storage Local Development, Live Code Reloading
Portability High (Easy to backup, restore, migrate) Low (Tied to specific host folder structure)
CLI Example -v my_volume:/app -v /host/path:/app

πŸ§ͺ Hands-on Lab: Try It Yourself

Follow these step-by-step exercises in your terminal.


πŸ”’ Part 1: Docker Volumes (Best for Production & Databases)

Docker Volumes are completely managed by Docker and isolated from the core host file system.

Step 1: Create a managed volume

docker volume create my_volume
Enter fullscreen mode Exit fullscreen mode

Verify that the volume was created:

docker volume ls
Enter fullscreen mode Exit fullscreen mode

Step 2: Start a container with the volume attached

docker run -d --name app_volume -v my_volume:/app nginx
Enter fullscreen mode Exit fullscreen mode

Step 3: Write data inside the container

docker exec app_volume bash -c "echo 'Persistent data from Volume' > /app/data.txt"
Enter fullscreen mode Exit fullscreen mode

Verify the file exists inside:

docker exec app_volume cat /app/data.txt
Enter fullscreen mode Exit fullscreen mode

Step 4: Simulate a crash / Destroy the container

docker rm -f app_volume
Enter fullscreen mode Exit fullscreen mode

Step 5: Attach the same volume to a NEW container

docker run -d --name app_volume_new -v my_volume:/app nginx
Enter fullscreen mode Exit fullscreen mode

Step 6: Verify data persistence

docker exec app_volume_new cat /app/data.txt
Enter fullscreen mode Exit fullscreen mode

Result: You will see Persistent data from Volume. The data survived the total destruction of the original container!

Cleanup Part 1:

docker rm -f app_volume_new
docker volume rm my_volume
Enter fullscreen mode Exit fullscreen mode

πŸ“‚ Part 2: Bind Mounts (Best for Local Development)

Bind Mounts directly link a folder on your host machine to a directory inside the container.

Step 1: Create a local directory on your host machine

mkdir -p ~/docker_lab/html
echo "<h1>Hello from Local Host (V1)</h1>" > ~/docker_lab/html/index.html
Enter fullscreen mode Exit fullscreen mode

Step 2: Start an Nginx container using the bind mount

docker run -d --name web_server -v ~/docker_lab/html:/usr/share/nginx/html -p 8080:80 nginx
Enter fullscreen mode Exit fullscreen mode

Step 3: Test access in your browser or terminal

curl http://localhost:8080
Enter fullscreen mode Exit fullscreen mode

Output: <h1>Hello from Local Host (V1)</h1>

Step 4: Edit the file locally on your host machine (no container restart!)

echo "<h1>Live update reflected instantly! (V2)</h1>" > ~/docker_lab/html/index.html
Enter fullscreen mode Exit fullscreen mode

Step 5: Verify immediate change

curl http://localhost:8080
Enter fullscreen mode Exit fullscreen mode

Output: <h1>Live update reflected instantly! (V2)</h1>

Cleanup Part 2:

docker rm -f web_server
rm -rf ~/docker_lab
Enter fullscreen mode Exit fullscreen mode

πŸ’‘ Summary & Takeaways / Resumen

  • Use Docker Volumes when you need secure, isolated, and high-performance storage for databases (e.g., PostgreSQL, MySQL, MongoDB) or production services.
  • Use Bind Mounts when developing code locally so your IDE edits immediately reflect inside the container without rebuilding images.

πŸ’¬ Discussion

Which persistence method do you use more in your daily workflow? Have you ever lost data due to container recreation? Let's discuss in the comments below! πŸ‘‡

#docker #devops #backend #learning

Top comments (2)

Collapse
 
raknaos profile image
Baptiste Le Bouquin

The destroy-the-container-and-reattach exercise is exactly the demo that makes persistence click, and the table matches how I ended up splitting usage in practice: named volumes for anything that must survive, bind mounts only where I want live-edit from the host.

A few gotchas I'd add if you extend it. First-use behavior differs in a way that surprises people: a named volume initialized over a non-empty image directory copies the image content in, while a bind mount shadows it β€” mount an empty host dir over /app and your app files "disappear". That asymmetry explains most of the "my files are gone" threads I've seen. Second, on a root-managed box, defaulting to :ro on bind mounts that don't need writes has saved me more than once β€” an agent or a misconfigured app happily writing into a host path is a bad afternoon. And docker volume rm refusing while a stopped container still references the volume is the classic cleanup trap: docker ps -a --filter volume=<name> before deleting, or docker system prune --volumes discipline.

Did you deliberately skip compose top-level volumes: and docker cp, or is that a part two? Compose named volumes shift the mental model slightly β€” the lifecycle gets tied to the project instead of the engine.

Collapse
 
jhosebro profile image
Jhoan Sebastian Rojas Holguin

That’s fantastic feedback! Thanks for taking the time to write such a thoughtful and value-packed comment.

You nailed the exact pain points that stump most people when moving beyond the basics:

  • The shadowing asymmetry: That "empty host directory shadowing /app" gotcha is indeed responsible for 90% of the initial panic posts on forums. It's one of those things you have to experience once to never forget.
  • Read-only mounts (:ro): Excellent point on security. Defaulting to :ro for bind mounts when write access isn't required is a top-tier safety practice.
  • Volume cleanup traps: docker volume rm complaining about stopped containers is a classic rite of passage!

Regarding Docker Compose (volumes:) and docker cp: yes, that was a deliberate choice! I wanted to keep this initial guide strictly focused on CLI fundamentals and the core mental model without overwhelming beginners.

Compose introduces project-level lifecycle management that definitely deserves its own dedicated breakdown. That will be Part 2 of this series!

Thanks again for adding so much depth to the post! πŸš€