Quick answer

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?
Not if the database uses a named volume. Docker’s options table describes –volumes as ‘Prune anonymous volumes’, and the confirmation prompt says it removes all anonymous volumes not used by at least one container. Named volumes are not anonymous. This is documented behaviour rather than a bench result on my part, so run the scratch test in this article before you bet a database on it.
What does docker system prune remove by default?
The confirmation prompt lists four things: all stopped containers, all networks not used by at least one container, all dangling images, and unused build cache. It does not touch volumes at all unless you add –volumes.
What does the -a flag add?
Docker’s options table describes it as ‘Remove all unused images not just dangling ones’. That is a much bigger sweep than it sounds. A dangling image is one no tag points at; an unused image is any image with no container associated with it, tagged or not. On a homelab where you pull images and run them only occasionally, -a will remove things you will have to pull again.
Which command deletes named volumes?
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. The –all flag is gated at API 1.42 and above. This is the command to be careful with, and it is worded more mildly than the one people are usually afraid of.
Why did a volume disappear when I never created one?
Some official images declare a VOLUME in their Dockerfile — Postgres declares one at its data directory. Run such an image without giving it a named volume or bind mount and Docker creates an ANONYMOUS volume to satisfy the declaration. That volume holds real data and is in scope for anonymous-volume pruning. The fix is to always name the volume yourself.
How do I see what is using the space first?
docker system df. It breaks usage into images, containers, local volumes and build cache, with a reclaimable column. Run it before any prune so you know whether your problem is images, cache, or one runaway log — pruning is a poor substitute for knowing.
How do I clear just the build cache?
docker builder prune. On machines that build often this is frequently the entire problem, and it is the safest of these commands because build cache is by definition reproducible.

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.

About the author

Written by Lowell K. Wood IV, who builds and runs TechFuelHQ from St. Louis, Missouri.