Docker Volumes Explained: Named, Bind, and tmpfs Storage
Containers are ephemeral by design — when you remove one, its filesystem disappears with it. That is fine for stateless apps, but databases, uploaded files, and logs need to survive restarts and redeploys. Docker volumes solve this by storing data outside the container layer. Docker vs Kubernetes explains how that container boundary changes once an orchestrator owns replacements.
This guide explains why volumes matter, the three storage types (named volumes, bind mounts, tmpfs), how to share data between containers, and the commands you use daily. If you have ever lost database data after docker rm, this is the fix. For the next operational layer, trace how Kubernetes reaches a workload in the Kubernetes request-flow guide.
Why Docker Volumes?
A container filesystem is a thin writable layer on top of an image. Anything written inside the container lives in that layer. When the container is deleted, the layer is deleted too — along with your database files, uploads, and logs.
A volume stores data on the host (or in Docker's managed storage area) and mounts it into the container at a path like /var/lib/mysql. The container can read and write the volume, but the data belongs to the volume — not the container. Delete the container, start a new one, mount the same volume, and your data is still there.
Quick reference
- Without volume: container deleted → data deleted.
- With volume: container deleted → data remains safe.
- Volumes survive container restarts, upgrades, and replacements.
- Images are immutable; volumes hold mutable runtime state.
- Use volumes for databases, file uploads, and application logs.
- Container storage is for temporary cache and build artifacts only.
Remember this
Volumes decouple data lifetime from container lifetime — essential for anything that must persist.
Named Volumes
Named volumes are created and managed by Docker. You give one a name (postgres_data), and Docker stores it outside the container's writable layer. Mount it at PostgreSQL's data path, and the database files persist when that container is replaced.
Named volumes are a portable default when Docker should manage the host location. They appear in docker volume ls, but they are not a backup: copy or snapshot the data separately and test a restore. The image process still needs permission to write the mounted path.
Quick reference
- Created with: docker volume create mysql_data
- Mounted with: -v mysql_data:/var/lib/mysql
- Managed by Docker — no host path required.
- Best for: PostgreSQL, MySQL, Redis persistence, uploads.
- Survives docker rm on the container.
- Reusable: new container mounts same volume by name.
Remember this
Named volumes are Docker-managed persistent storage — the production default for databases.
Bind Mounts and tmpfs
Not every mount is a named volume. A bind mount maps a specific host directory into the container — for example -v ./src:/app/src. Edit files on your laptop and see changes instantly inside the container. Bind mounts are ideal for local development where you want live code reload without rebuilding the image.
A tmpfs mount is memory-backed and disappears when the container stops. Use it for temporary files or scratch data that should not persist in the container layer. It reduces ordinary disk persistence, but host swap and runtime security still matter; do not treat tmpfs as a complete secrets-management system.
Quick reference
- Bind mount: host path → container path (-v /host:/container).
- Bind mount: host changes appear inside container immediately.
- Bind mount: risky in production — host path coupling.
- tmpfs: data lives in RAM — gone when container stops.
- tmpfs: useful for temporary sensitive data, but use Docker/Compose secrets or an external secret manager for delivery and access control.
- Named volume > bind mount for production database data.
Remember this
Bind mounts aid development; tmpfs is ephemeral scratch space; secret management still needs a dedicated mechanism.
Bind Mounts in Practice
Bind mounts create a direct bridge between a host folder and a container path. Your project directory on disk becomes /app inside the container. Tools like Docker Compose use bind mounts heavily in development: mount source code, run the app in a container, edit files in your IDE, and the running process picks up changes.
The trade-off is coupling. The container now depends on a specific host path existing. That path differs between developers' machines and does not exist in CI or production the same way. Never bind-mount production database directories — use named volumes instead.
Quick reference
- Syntax: -v /absolute/host/path:/container/path
- Relative paths work: -v ./data:/app/data
- Read-only bind: append :ro to the mount.
- Compose: volumes: - ./src:/app/src in service definition.
- Watch out for file permission mismatches (UID/GID).
- Production: prefer named volumes over bind mounts.
Remember this
Bind mounts trade portability for dev speed — perfect locally, avoid in production.
Essential Volume Commands
Docker provides a small CLI for volume management. List all volumes with docker volume ls. Create one explicitly with docker volume create mydata before running a container, or let Docker auto-create it on first mount. Inspect a volume to see its mount point on the host with docker volume inspect mydata.
Remove a single volume with docker volume rm mydata — only when no container is using it. Clean up all unused volumes at once with docker volume prune (careful: this deletes data). Remember: deleting a container does not delete its volumes unless you pass --volumes to docker rm or docker compose down -v.
Quick reference
- docker volume ls — list all volumes.
- docker volume create mydata — create named volume.
- docker volume inspect mydata — show mount point and driver.
- docker volume rm mydata — delete one volume.
- docker volume prune — remove all unused volumes.
- docker rm -v — remove container AND its anonymous volumes.
- docker compose down -v — remove compose volumes too.
Remember this
Delete container ≠ delete volume — use docker volume rm or prune explicitly.
Key takeaway
Use named volumes when data must outlive a container, bind mounts when a host path is intentionally part of the development workflow, and tmpfs for non-persistent scratch data. Persistence is not backup: protect important volume data with a separate, tested restore process.
Practice (25 min): Run the PostgreSQL workflow above, insert a row, remove the container, and mount postgres_data into a replacement; the row should still be present. Intentionally run docker volume rm postgres_data while it is attached and use the error to identify the owning container. Recover by preserving the named data volume, removing only the disposable container, and deleting a separate scratch volume. Pass when the replacement returns the row and docker volume ls proves only the intended scratch volume was deleted.
Related Articles
Explore this topic