📑 Daftar Isi
Please, don’t ever take this command for granted. docker compose down looks like an innocent cleanup command to most people. But the moment a -v sticks to the end of it, it turns into one of the meanest data-destroying tools I know. I’m not exaggerating — last month I picked up a ticket where a client’s entire database vanished because of a single line like this.
And here’s the part that stings the most. There was a second case where the engineer actually got lucky. He meant to reset his environment, typed what he thought was the right command, and it turned out he was missing one flag. That missing flag is exactly what saved him. He didn’t use the -v option when running docker compose down, and that single omission kept his volumes alive.
Before we get into the case studies, let me clear up how these two commands actually work, because almost every incident starts with a misunderstanding right here. docker compose down stops and removes the containers created by your compose file, plus the default networks Compose spins up. The part people keep getting wrong: named volumes you declare in the volumes: section of your compose file do NOT get removed. Your data stays put. But if you add the -v or --volumes flag, Docker removes those named volumes too, along with any anonymous volumes attached to the containers. That’s when your database data, user uploads, or app caches vanish in seconds — no confirmation, no undo.
And the impact goes way beyond “we lost some files.” In the case I handled, the client had to wait three days while the dev team restored data from a backup that turned out to be a week old. Revenue lost, user trust down the drain, and the ops team had to work overtime explaining what actually happened. All because one person didn’t know the difference between down and down -v.
You might think, “come on, how do you mix those up, it’s just one extra letter.” Believe me, it happens way more often than you’d guess. The usual culprits: copy-pasting snippets from tutorials that casually write docker compose down -v as the reset command, shell aliases that flip things around, or cleanup scripts written to do a full wipe without ever adding a guard. From what I’ve seen in the field, the top two causes are copied docs and aliases that hide how destructive a command really is. Both are easy to prevent once you understand the risk. Let me walk you through real cases — from the lucky one to the painful one.
Case 1: Saved Because -v Was Missing
The first case honestly made me think “lucky mistake” should be a thing. A team wanted to clean up their dev environment, which was full of leftover containers. They copied a cleanup command from an internal repo, and it was just docker compose down with no extra flags. It ran, all containers stopped and got removed. Then, right when they sat down to keep working, they realized they’d run it against the wrong stack — a stack holding a dev database that had been in use for a week of testing.
Panic? Yeah, a little. But before anyone started crying, one of them remembered to check the volumes. And this is the moment that matters: docker volume ls showed every named volume still there. Data intact. Why? Because they didn’t use -v when running docker compose down. Only the containers and networks were removed; the volumes stayed on disk. They just ran docker compose up -d, and everything came back like nothing ever happened.
Here’s the first lesson, write this down: docker compose down without -v keeps your volumes. It’s a safe way to reset your app state, because the data in your named volumes survives and gets remounted when containers come back. The dangerous ones are the -v variants, or the combo of running down and then docker volume prune.
Case 2: One -v and the Data Goes Under
The second case is the exact opposite, and honestly, it’s the one that gets me the most fired up. A fellow engineer followed a “clean up docker” tutorial from some blog (not this one, ha). The author casually wrote docker compose down -v as the way to do a full reset, with zero warnings. My friend ran it on a stack that had been serving a client project for two months, including a PostgreSQL volume with important testing data.
The result? Containers gone, networks gone, the PostgreSQL volume gone. When he ran docker compose up -d again, the database that came up was empty, like a fresh install. He only realized it four hours later, and the last backup was a week old. Explaining that to the client? Not fun. Roughly a week of work just disappeared, and he had to own up to the team.
docker compose down -v on an environment that holds data you actually need — unless you have a backup you can trust. Once a volume is removed, it doesn’t come back. This isn’t the recycle bin.That case taught me something: tutorials without warnings can be genuinely dangerous. If you write docs or snippets for your team, always spell out the difference between down and down -v. It genuinely helps someone else down the line.
Case 3: The Sneaky down + prune Combo
This third one is sneakier, because no single command was obviously wrong — it was the order that did the damage. Someone ran docker compose down (without -v) to free up server resources. Once the containers were gone, those named volumes became unused, meaning no container referenced them anymore. Later, during routine maintenance, someone ran docker volume prune to tidy up docker leftovers, not realizing those idle volumes were still important.
And that’s the trap. docker volume prune removes ALL volumes that aren’t attached to any container. And after docker compose down, every volume in your stack falls into that category. So down first, prune later — the end result is just as bad as down -v. What makes it worse is that splitting it across two commands makes it look safe, when the effect is cumulative.
Lesson from case three: don’t prune blindly after resetting a stack. Always check which volumes are still in use first. I’ve put the exact commands in the verification section below so you can decide calmly instead of guessing.

How to Verify Volumes Before and After
Alright, now the actually useful part. Before you run any destructive command that touches docker volumes, get into the habit of verifying first. Here’s the checklist I use with my team:
- List running containers:
docker ps -a --format '{{.Names}} {{.Image}}' - Check each container’s mounts:
docker inspect <container> --format '{{range .Mounts}}{{.Name}} -> {{.Destination}}{{end}}' - List all volumes:
docker volume ls - Inspect a specific volume:
docker volume inspect <volume-name> - If you use Compose, check the volume definitions:
docker compose config --volumes
Here’s what docker volume inspect outputs — pay attention to Mountpoint:
$ docker volume inspect db_data
[
{
"CreatedAt": "2026-06-11T08:14:32Z",
"Driver": "local",
"Mountpoint": "/var/lib/docker/volumes/db_data/_data",
"Name": "db_data",
"Options": null,
"Scope": "local"
}
]
If the volume still shows up in docker volume ls after you run docker compose down, you’re fine — your data is sitting safely in /var/lib/docker/volumes/<name>/_data. Just bring the stack back up and the container will remount the same volume. But if the volume no longer shows up in the list, it’s gone, and you need to head straight to your backups.
If you want to go deeper on backing up and restoring volumes, I wrote a dedicated guide on docker volume backup and restore. And if you want to run Compose safely in production, check out my docker compose production best practices.
Command Comparison Table (The Ones People Mix Up)
| Command | Removes Containers | Removes Networks | Removes Anonymous Volumes | Removes Named Volumes | Removes Bind Mounts |
|---|---|---|---|---|---|
docker compose down |
Yes | Yes | Yes | No | No |
docker compose down -v |
Yes | Yes | Yes | Yes | No |
docker rm <container> |
Yes | No | No | No | No |
docker rm -v <container> |
Yes | No | Yes | No | No |
docker volume prune |
No | No | Yes (unused) | Yes (unused) | No |
docker system prune -a --volumes |
Yes (stopped) | Yes | Yes | Yes (unused) | No |
Read that table slowly, because it’s the core of every case above. Everything in the second and last rows is what makes people cry. No wonder so many folks say docker volumes are a double-edged sword: they can make your data immortal, and they can make it disappear.
Back Up Volumes Before Destructive Commands
If you really must do a full reset, get into the habit of backing up first. The simplest way to back up a docker volume is with a throwaway alpine container:
docker run --rm
-v db_data:/data
-v /home/ops/backup:/backup
alpine tar czf /backup/db_data-$(date +%F).tar.gz -C /data .
That packs the entire db_data volume into /home/ops/backup/db_data-2026-07-01.tar.gz. To restore, just do the reverse:
docker run --rm
-v db_data:/data
-v /home/ops/backup:/backup
alpine sh -c "rm -rf /data/* && tar xzf /backup/db_data-2026-07-01.tar.gz -C /data"
Important note: stop any container using the volume before restoring, so nothing writes to the files while you overwrite them. And if you’re backing up a database like MySQL or PostgreSQL, prefer the built-in dump tools — pg_dump for Postgres, mysqldump for MySQL — for consistent snapshots. I’ve also got a MySQL database backup guide if you want the automated approach.
Prevention So It Doesn’t Happen Again
Here are some practical steps you can apply today so this never lands on your desk:
- Keep
-vout of automated scripts. Unless you have a clear reason to delete volumes, never put the-vflag in a cleanup script. Make it a separate script that requires manual confirmation. - Add a confirmation step. Use an interactive prompt before any destructive command runs. Being two seconds slower beats losing data.
- Don’t alias
downtodown -v. If you want to run a regular down, typedocker compose downas is. Don’t build shortcuts that secretly destroy things. - Schedule backups. Any environment holding valuable data deserves routine backups, whether via cron or a dedicated tool. Backups are the cheapest insurance you’ll ever buy.
- Check compose config first. Before running anything, make sure
docker compose configshows the correct volume definitions, so you don’t hit the wrong stack.
If you want to get better at reading docker logs to trace issues like these, I have an article on docker log troubleshooting that walks through it. Every problem I mentioned above shows up early if you actually read the output and logs carefully.
Q: Does docker compose down delete volumes?
Not by default. docker compose down removes containers and networks, but named volumes declared in your compose file survive. Only anonymous volumes get removed without -v. To remove named volumes, you need docker compose down -v or --volumes.
Q: What’s the difference between down, down -v, and docker volume prune?
down cleans up containers and networks while keeping named volumes. down -v cleans up everything including named volumes. docker volume prune deletes every volume not attached to any container, so it becomes dangerous if you run it right after down.
Q: Do bind mounts get deleted by down -v?
No. Bind mounts are host directories mounted into the container, like -v /home/user/data:/var/lib/mysql. Docker never deletes host folders, so your bind-mount data survives down -v.
Q: How do I check if a volume still exists after docker compose down?
Run docker volume ls and look for your volume name. For details, docker volume inspect <name> shows the mountpoint and data location. If it doesn’t show up, the volume has been removed.
Q: Can I recover a deleted docker volume?
It’s extremely hard and not guaranteed. Removed docker volumes are usually deleted from disk as well, for example in the thin pool. The most realistic path is restoring from backup. That’s why backing up before any destructive command isn’t optional — it’s mandatory.
Please don’t repeat mistakes like these. Learn the difference between down and down -v before you touch a production server. One letter can change everything: your data survives, or your data is gone. Take it seriously. If you’ve got your own docker volume horror story, drop it in the comments so the rest of us can learn from it.