Docker Volumes
A container's writable layer disappears when the container is removed, and it's slow for heavy writes anyway. When data needs to outlive a container (a database's files, uploaded assets) or be shared with the host (your source code during development), you mount storage into the container. Docker offers three kinds: volumes, bind mounts, and tmpfs mounts.
Picking the right one decides whether your data survives, whether file permissions work, and whether development on macOS feels instant or sluggish.
TL;DR
- Named volumes are managed by Docker, stored in Docker's area, and are the default choice for persistent app data such as databases.
- Bind mounts map a specific host path into the container. Use them for source code, config files, and sharing with host tools.
- tmpfs mounts live in memory and vanish on stop. Use them for scratch space and sensitive temporary files.
- Prefer the explicit
--mountsyntax over-vfor clarity. - Removing a container doesn't delete its named volumes;
docker volume pruneandcompose down -vdo. - File ownership is by numeric UID, so match the container user to avoid "permission denied".
Quick Example
Remove and recreate the db container and the data is still there, because it lives in the pgdata volume rather than in the container.
Core Concepts
The Three Mount Types
Named vs Anonymous Volumes
A named volume (pgdata) is easy to find, reuse, and back up. An anonymous volume gets a random ID. Images that declare VOLUME /data create one automatically if you don't mount anything there. Anonymous volumes pile up silently; name your volumes.
When you mount an empty named volume at a path where the image already has files, Docker copies those files into the volume first. Bind mounts don't do this: they hide whatever the image had at that path.
-v vs --mount
They're equivalent for volumes, with one trap: with -v, a bind-mount source that doesn't exist is silently created as an empty directory, while --mount errors out. Explicit is safer.
Volume Drivers
The default local driver stores data on the host. Plugins and driver options can back volumes with NFS, CIFS, or cloud block storage, though for multi-host storage most teams move to an orchestrator's storage layer (Kubernetes storage).
Permissions and UIDs
Linux file permissions use numeric user IDs, not names. If your container runs as UID 1000 but a bind-mounted directory is owned by UID 501, writes fail with permission denied. Options:
- Run the container as your host UID for development:
--user "$(id -u):$(id -g)". chownthe path to the container's UID in the image or an init step.- Use named volumes, which Docker initializes with the image's ownership for that path.
- On SELinux hosts (Fedora, RHEL), add
:zor:Zto bind mounts so the container is allowed to read them.
Backups and Migration
A volume is just a directory, so back it up by mounting it into a throwaway container:
For databases, prefer the database's own tools (pg_dump, mysqldump, physical backups with WAL) over copying files from a running server. File copies of a live database can be inconsistent. See database backups.
Best Practices
Use Named Volumes for Data, Bind Mounts for Code
Databases and stateful services get named volumes; development source trees get bind mounts or Compose watch. Don't bind-mount a database's data directory into your project folder, where it'll be accidentally committed, deleted, or synced.
Mount Config Read-Only
Add readonly (or :ro) to config and secret files. A compromised process then can't rewrite its own configuration.
Clean Up Deliberately
docker system df -v shows volume usage. docker volume prune removes volumes no container uses (by default only anonymous ones in recent Docker versions; add --all for named ones). Name your important volumes so they're easy to recognize before pruning.
Keep Write-Heavy Paths Off the Container Layer
Logs, caches, and temp files written to the container's writable layer go through the storage driver, which is slower and grows the container. Mount a volume or tmpfs for them, or log to stdout.
Common Mistakes
Slow Bind Mounts on macOS and Windows
On Docker Desktop, containers run in a Linux VM, so bind mounts cross a file-sharing boundary. Tools that touch thousands of files, like node_modules or Python virtualenvs, get dramatically slower.
Enabling VirtioFS and synchronized file shares, or using Compose watch, helps further.
Losing Data With --rm and Anonymous Volumes
docker run --rm deletes anonymous volumes when the container exits. If an image declares VOLUME /data and you didn't mount a named volume there, that data is gone.
Bind-Mounting Over Files the Image Needs
Mounting ./:/app hides everything the image put in /app, including installed dependencies or build output, and replaces it with your host directory. Mount narrower paths, or add a volume for the directories the image must keep.
FAQ
What's the difference between a volume and a bind mount?
A volume is storage Docker creates and manages in its own area; you refer to it by name and Docker handles its location and initial contents. A bind mount maps an exact host path you choose. Volumes are more portable and better for app data; bind mounts are best when the host also needs to read or edit the files, such as source code.
Where are Docker volumes stored?
On Linux, under /var/lib/docker/volumes/<name>/_data by default. On Docker Desktop they live inside the Linux VM, not directly on your Mac or Windows filesystem, so use a helper container or docker cp to access them.
Are volumes deleted when I remove a container?
Named volumes are not. Anonymous volumes are removed only if you use docker rm -v or ran the container with --rm. docker compose down keeps volumes, while docker compose down -v deletes them.
Can two containers share a volume?
Yes. Mount the same named volume into both. That works for sharing files, but concurrent writes to the same files need coordination; two database servers must never share one data directory.
Related Topics
- Docker — The container platform overview
- Docker Compose — Declaring volumes for multi-container apps
- Kubernetes Storage — Persistent volumes in a cluster
- Database Backups — Backing up data safely
- Backup Strategy — The 3-2-1 rule and beyond