AuthorVeraStreamEngineeringDepth2 — working knowledgeTypeField note
Docker has three storage mechanisms — named volumes, anonymous volumes, and bind mounts — and they have three completely different lifecycles. Most Docker documentation treats them as variations of the same thing. They are not.
Named volumes
A named volume is managed by Docker and persists independently of any container. Creating a container with -v mydata:/var/lib/postgresql/data creates a named volume called mydata if it does not already exist. When the container is stopped, the volume persists. When the container is deleted, the volume persists. When you run docker compose down, the containers are removed — but the volumes are not.
# This does NOT remove named volumes
docker compose down
# This DOES remove named volumes
docker compose down -vThis is intentional. Named volumes are designed to hold data that outlives containers. The design assumption is that you want your database data to survive a container restart.
Anonymous volumes
An anonymous volume is created when a Dockerfile contains a VOLUME instruction or when you use -v /container/path without naming the volume. Docker generates a random hash as the name. Anonymous volumes survive container stop but are removed by docker compose down without -v — the opposite behavior from named volumes. They are also removed by docker container rm.
The practical problem: if a third-party image declares a VOLUME in its Dockerfile, your docker compose down leaves behind the named volumes you defined, but Docker handles the anonymous volumes automatically. This inconsistency confuses every developer who encounters it.
Bind mounts
A bind mount maps a host directory into a container. Docker has no lifecycle management over bind mounts at all. The data lives on the host filesystem; Docker cannot and does not track or remove it. docker compose down -v does nothing to bind-mounted directories.
The common mistake
You have a Postgres service in docker-compose.yml with a named volume for the data directory. You run docker compose down && docker compose up expecting a fresh database — maybe to test a migration from scratch. The containers recreate, but the named volume persists. Your database is not empty. It has all the data from your previous session.
This surprises developers because the mental model ("I restarted Docker Compose, so everything is fresh") does not match Docker's actual behavior ("I restarted the containers, not the storage").
The dangerous mistake in production
Knowing that docker compose down -v removes volumes, a developer runs it to "clean up" a staging environment. They do not realize that the same command, run on production with the same docker-compose.yml, would delete the database. The flag is the same. The consequence is not.
Finding orphaned volumes
# See all volumes, including ones no container uses
docker volume ls
# Remove volumes not used by any container (interactive confirmation)
docker volume prune
# Nuclear: remove ALL unused volumes without confirmation
docker system prune --volumesdocker system prune --volumes is the command most likely to cause an unrecoverable data loss incident in local development. It removes every volume not attached to a running container. If you ran docker compose down before running it, none of your volumes are attached to running containers.
Backing up a named volume
docker run --rm -v myvolume:/data -v $(pwd):/backup alpine tar czf /backup/myvolume.tar.gz -C /data .This creates a temporary Alpine container, mounts the named volume at /data and the current directory at /backup, and archives the volume contents. The temporary container is removed when it exits. This is the safest way to back up a named volume before destructive operations.
When to use each
- Named volumes: database data, persistent application state, anything that must survive container recreation
- Bind mounts: source code in development (live code reload), config files you edit from the host, log directories you want to access directly
- Anonymous volumes: avoid creating them intentionally; they appear when third-party images declare VOLUME in their Dockerfiles
The rule is simple: if you need the data to survive a docker compose down, it should be in a named volume or a bind mount — and you should know which one you chose, because only one of them is managed by Docker's lifecycle commands.
Building something? builds.anethoth.com is a public build ledger — proof that a product is really being built.