docker system prune –volumes removes ANONYMOUS volumes only — Docker’s own flag description says so, and the confirmation prompt repeats it. A named volume, which is what any sane compose file gives a database, is out of scope. The command that can delete named volumes is docker volume prune –all, and it sounds narrower than the one everybody fears.
By LK Wood IV · 2026-08-15 · ~8 min read · St. Louis County, MO
The received wisdom is simple. docker system prune -a --volumes eats databases, so never run it.
That fear is aimed at the wrong command. Docker’s own options table describes --volumes as “Prune anonymous volumes”, and the confirmation prompt spells out what it will remove: all anonymous volumes not used by at least one container. A named volume is not anonymous. If your Postgres data lives in a volume you named in a compose file, docker system prune -a --volumes is not the thing that will take it.
The command that removes named volumes is this one.
docker volume prune --all
Docker documents that by default docker volume prune only removes anonymous volumes, and that --all removes all unused volumes, not just anonymous ones, behind an API 1.42 gate. Read those two sentences next to each other and the asymmetry is obvious: the destructive option is a four-character flag on a command whose name sounds like it only touches leftovers.
So the scary-sounding command is narrower than its reputation. The mild-sounding one is the loaded gun. That inversion — feared command safe, safe-sounding command dangerous — is the whole reason this page exists.
One honest caveat before you act on any of it. Everything above is read from Docker’s current documentation, not from a test I ran on this machine. I have no Docker daemon here, and I would rather say so than imply a bench I did not use. There is a two-minute scratch test at the end. Run it first. Trust it over me.
Look before you sweep
docker system df
That breaks usage into images, containers, local volumes and build cache, with a reclaimable column. Run it first. Every time.
I push this because “reclaim disk space” is usually the wrong framing, and running a prune before you have looked at docker system df is how people end up re-pulling forty gigabytes of images to solve a problem that was one container writing an unbounded log file the whole time. The question is what filled the disk, and the answer changes the fix entirely. Build cache on a machine that builds often is a different problem from forty pulled images you never run, which is different again from one container writing an unbounded log. Only one of those is solved by pruning.
What each command removes
docker system prune, per its confirmation prompt, removes exactly four things:
- all stopped containers
- all networks not used by at least one container
- all dangling images
- unused build cache
No volumes at all. A dangling image — one that no tag points at any more — is typically what is left after rebuilding under the same tag.
docker system prune -a adds, per the options table: Remove all unused images not just dangling ones.
This is a much wider sweep than it reads. “Unused” means no container is associated with the image, tagged or not. On a homelab where you keep images around for things you run occasionally, -a removes them and you will pull them again over your home connection at the least convenient moment. It is safe for your data. It is expensive for your time.
docker system prune --volumes adds anonymous volumes, as covered above.
docker volume prune takes anonymous volumes only, by default.
docker volume prune --all is the one to watch. Unused named volumes go too.
docker builder prune takes build cache only. It is the safest command here, because build cache is reproducible by definition. On a machine that builds regularly it is also often the entire problem.
The trap that is genuinely real
Here is the case where you can genuinely lose data to anonymous-volume pruning. It is not the one people worry about.
Some official images declare a VOLUME in their Dockerfile. Postgres declares one at its data directory. When you start such an image without giving it a named volume or a bind mount, Docker satisfies that declaration by creating an anonymous volume. It has a long hex name you never chose. It holds your actual database.
# creates an anonymous volume holding real data
docker run -d postgres:17
# creates a named volume you control
docker run -d -v pgdata:/var/lib/postgresql/data postgres:17
Stop that first container and the anonymous volume is now unused, and squarely in scope for --volumes.
So the failure mode is not “prune ate my named volume”. It is “I never named the volume, so Docker quietly named it for me, that autogenerated name made it anonymous by definition, and anonymous is precisely the category the flag I ran was documented to remove”. The fix is upstream of any prune command: name your volumes. A compose file with a top-level volumes: block gets this right by default, which is one more reason to run things through compose rather than long docker run lines.
What I run weekly
Weekly, on a homelab Docker host, with the reasoning attached:
docker system df # look first
docker builder prune # cache is reproducible
docker system prune # stopped containers, unused networks, dangling images
That reclaims most of what accumulates. It touches nothing you cannot rebuild.
I add -a only when I have decided I am willing to re-pull, which on a metered or slow connection is a real cost rather than a theoretical one.
I never run docker volume prune --all on a schedule. A volume is the one thing here that might be the only copy. When I want a specific volume gone, I look at it first and then remove it by name, which is slower in exactly the way that a command capable of destroying the only copy of something ought to be:
docker volume ls
docker volume inspect <name>
docker volume rm <name>
Slower. It also makes me say what I mean.
The scratch test, so you do not have to take my word for it
Two minutes, on a machine where losing the test volumes costs nothing.
docker volume create keepme
docker run -d --name anon-test postgres:17 # anonymous volume
docker run -d --name named-test -v keepme:/data alpine sleep 60
docker rm -f anon-test named-test
docker volume ls # note what exists
docker system prune -a --volumes # the feared command
docker volume ls # keepme should still be here
If keepme survives and the hex-named one does not, the documented behaviour holds on your version. If it does not survive, I want to know — that would mean the docs and your daemon disagree, which is a much more interesting problem than a tidy disk.
What I have not tested
I have not run any of this on a live daemon during this write-up, for the reason given at the top. Every behavioural claim here traces to Docker’s current CLI documentation.
I have not checked how an older daemon behaves, and this is the gap I would most want closed before anyone runs these commands on a long-lived box that has not been updated in a couple of years. --all on docker volume prune carries an API 1.42 gate, which implies the pre-1.42 behaviour differed, and I have not established what a new CLI does against an old daemon. If you are on something long-lived and unpatched, test rather than assume.
I have not measured how much any of this reclaims in practice. That number is a property of your machine, and quoting mine would tell you nothing.
What getting it wrong costs
Pruning images costs you a download. Pruning build cache costs one slow build. Both are annoyances with a known price.
Pruning a volume costs you whatever was in it, and the price is only discovered later, usually when something tries to read data that is no longer there and fails in a way that does not obviously say “your volume is gone”. If that data mattered, the real lesson is not about prune flags at all. It is that a volume you would miss should be backed up, because a wrong flag is only one of many ways to lose it.
Frequently asked questions
Does docker system prune -a --volumes delete my database volumes?
What does docker system prune remove by default?
What does the -a flag add?
Which command deletes named volumes?
Why did a volume disappear when I never created one?
How do I see what is using the space first?
How do I clear just the build cache?
Evidence ledger
- Last updated
- Methodology
- This tutorial was written and edited by Lowell K. Wood IV in St. Louis County, MO. Specs and prices verified against vendor and project documentation current on the date above. Full editorial standard: methodology.
- Update log
- 2026-08-15 — Last reviewed and updated.
- Corrections
- Spotted an error or a stale number? Email hello@techfuelhq.com. Confirmed corrections are added to the update log above.