Docker Named Volumes: The Complete Guide
Named volumes are Docker's managed storage for persistent data. Learn how to create, inspect, mount, back up, and clean up named volumes — and when to choose them over bind mounts.
Published on • October 28, 2026
AI Assistant

Containers are ephemeral by design — rewrite the image, and everything inside the container’s filesystem is replaced. Except when it shouldn’t be: your Postgres data, your uploaded files, your Redis queue. Docker’s answer is named volumes — directories managed by Docker, mounted into containers, and living independently of container lifecycle. They’re the default choice for persistent data in Docker, and this guide covers the full surface: creation, mounting, inspection, backup, and cleanup.
What a named volume actually is
A named volume is a directory under Docker’s management (by default /var/lib/docker/volumes/<name>/_data on Linux hosts) that you reference by name rather than by path. Compare the three storage options:
- Anonymous volume — a directory Docker creates and manages, but you don’t name. Hard to reuse or back up; mostly an implementation detail of images that declare
VOLUME. - Named volume — same mechanism, but you name it, so multiple containers can share it, and you can manage it with
docker volumecommands. Managed, portable across hosts, works without the host path being identical. - Bind mount — a direct mount of a specific host path (
/home/me/data) into the container. Simple, but couples the container to that host’s filesystem layout, and Docker doesn’t manage the data.
The decision rule: named volumes for data Docker should manage (databases, app state); bind mounts for development workflows where you want to edit files on the host and see them in the container.
Creating and inspecting
docker volume create app-data # create with optional driver/options
docker volume ls # list all volumes
docker volume inspect app-data # metadata: mountpoint, driver, labels
docker volume rm app-data # remove (fails if in use)
docker volume prune # remove ALL unused volumes — carefully
inspect is the debugging workhorse: it shows the actual host path (Mountpoint) and which driver manages it, answering “where is my data really?” — the question you ask when a volume seems to have vanished.
Mounting: the two syntaxes and their traps
In a Compose file:
services:
db:
image: postgres:16
volumes:
- app-data:/var/lib/postgresql/data # named volume, source first
The - source:target short syntax is fine for volumes, but the long mount syntax is worth learning because it makes a common footgun explicit:
volumes:
- type: volume
source: app-data
target: /var/lib/postgresql/data
volume:
nocopy: true
The footgun: when Docker first creates a named volume and mounts it at a target directory that already has content in the image, it copies the image’s content into the empty volume (the “copy-on-first-use” behavior). Usually harmless — sometimes catastrophic (an empty config directory in your image silently shadowing real defaults). nocopy: true disables it, mounting the volume truly empty.
The other classic trap is writing data to a path you didn’t intend to persist. If your app writes to /data but your volume mounts at /var/lib/app, the data is in the container layer and dies with it. Verify with docker inspect <container> → Mounts that the writable data path is actually inside the mount.
Lifecycle rules
docker rm <container>does not remove named volumes attached to it. They persist until explicitly removed — by design.docker volume pruneremoves every volume not attached to any container, including ones from stopped-but-existing containers’ past projects. Scope it (docker volume prunein a shared daemon is a Monday-morning incident generator) or delete specific volumes by name.- Volume names collide per daemon: two Compose projects using
app-datashare the same volume. Prefix names with your project (myapp_pgdata) unless cross-project sharing is the goal.
Backing up and migrating
Named volumes make backup a one-liner via a temporary container:
# Backup
docker run --rm -v app-data:/data -v "$PWD":/backup alpine \
tar czf /backup/app-data-$(date +%F).tar.gz -C /data .
# Restore
docker run --rm -v app-data:/data -v "$PWD":/backup alpine \
tar xzf /backup/app-data-2026-10-28.tar.gz -C /data .
The --rm container mounts the volume and the backup directory, and tar does the rest — no need for the app image to be running, or even present. This also handles migration between hosts: back up on one, restore on the other.
When things go wrong
Data “missing” after a redeploy usually means the volume name changed (Compose project rename, edited volumes: key) — a new volume was created and populated from the image, while the old one sits orphaned in docker volume ls. Performance surprises in I/O-heavy workloads sometimes warrant investigating the volume driver (default local vs plugin drivers like local on tmpfs or cloud drivers). And “device or resource busy” on removal means a container still references it — find it with docker ps -a and the Mounts section of docker inspect.
The bottom line
Named volumes are Docker’s contract for persistence: you name the data, Docker manages its location and lifecycle, and containers come and go around it. Create with docker volume create (or let Compose do it), mount with care for the nocopy behavior, back up with a one-line tar, and clean up deliberately — prune has no undo. Used well, they’re the difference between a stateless deployment you can trust and a database that “works until the container restarts.”