How Docker volume lifecycles work, why --volumes is dangerous, and how to protect persistent data.

You remove and rebuild your Compose app:

docker compose down docker compose up -d --build 

The containers are brand new, the image is rebuilt, and yet your database, accounts, and settings are all still there.

Is Docker failing to clean up? No. This is exactly how persistent storage is designed to work.

1. Containers and volumes have different lifecycles

A container is disposable. A volume is built to outlive it.

services: app: image: alpine:3.20 command: ["sleep", "infinity"] volumes: - app-data:/data volumes: app-data: 

Here app-data is a named volume. Compose creates it and mounts it at /data. When the container is removed, the volume stays on the Docker host.

This is what makes databases, uploads, and configuration survive upgrades.

2. What does docker compose down remove?

docker compose down docker compose down --volumes
Containers Removed Removed
Compose-managed networks Removed Removed
Named volumes (declared in top-level volumes) Kept Removed
Anonymous volumes Kept (not reattached by the next up) Removed
external: true volumes Kept Kept
Host bind mounts Kept Kept

Both commands remove containers. The only difference is whether Compose also deletes its persistent storage.

Warning: down --volumes (or -v) permanently deletes that data.

3. Verify it yourself

Start the app and write a file into the volume:

docker compose up -d docker compose exec app sh -c 'echo "Persistent data!" > /data/check.txt' 

Remove and recreate the app:

docker compose down docker compose up -d docker compose exec app cat /data/check.txt 

Output:

Persistent data! 

Now remove the volumes too:

docker compose down --volumes docker compose up -d docker compose exec app cat /data/check.txt 

This time you get No such file or directory: the old volume was deleted and up created a new, empty one.

This is why rebuilding an image never resets your database. Images and volumes are separate things.

4. A safer approach: external: true

For data you cannot afford to lose, manage the volume outside the Compose lifecycle.

Create it once:

docker volume create production-data 

Then reference it:

services: app: image: alpine:3.20 command: ["sleep", "infinity"] volumes: - app-data:/data volumes: app-data: external: true name: production-data 

With external: true, Compose treats the volume as an existing resource it does not own:

  • docker compose down --volumes will not delete it.
  • If the volume does not exist, docker compose up fails with an error instead of silently creating an empty one. That is a feature: a typo in the name will not give you a blank database.

Note that external: true protects against Compose, not against everything. docker volume rm and docker volume prune can still delete the volume, and it is not a backup.

5. Common commands at a glance

Command Removes containers Removes Compose-managed named volumes
docker compose stop No No
docker compose down Yes No
docker compose up -d --build Recreates when needed No
docker compose down -v Yes Yes

Bind mounts (./data:/var/lib/app) are a different mechanism. Compose never deletes the host directory.

6. Production checklist

Before updating or removing any stateful stack:

  1. List volumes with docker volume ls.
  2. Check what each container actually mounts with docker inspect <container>.
  3. Keep deployment (the Compose file) and data (the volume) separate where possible.
  4. Keep independent, tested backups of databases and application data.
  5. Never use down -v as a routine update command.

Pitfall: changing the project name

Compose prefixes managed volume names with the project name, so app-data becomes myproject_app-data. If you rename the project directory or set a different -p / name:, Compose creates a new, empty volume and your data appears to vanish. The old volume still exists; find it with:

docker volume ls 

Mount it again by restoring the original project name, or reference it explicitly with external: true.

Final thoughts

A container is not your data.

  • Use docker compose down to remove the stack and keep its volumes.
  • Use docker compose down -v only when you deliberately want to destroy Compose-managed data.
  • Use external: true when storage should have its own lifecycle.

Knowing the difference can save you from one of the most expensive mistakes in container administration: deleting production data with a single flag.