DEV Community

Frank Zhang
Frank Zhang

Posted on

Why Does Docker Compose Keep Your Data After `down`?

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
Enter fullscreen mode Exit fullscreen mode

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:
Enter fullscreen mode Exit fullscreen mode

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'
Enter fullscreen mode Exit fullscreen mode

Remove and recreate the app:

docker compose down
docker compose up -d
docker compose exec app cat /data/check.txt
Enter fullscreen mode Exit fullscreen mode

Output:

Persistent data!
Enter fullscreen mode Exit fullscreen mode

Now remove the volumes too:

docker compose down --volumes
docker compose up -d
docker compose exec app cat /data/check.txt
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Then reference it:

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

volumes:
  app-data:
    external: true
    name: production-data
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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.


Top comments (0)