Quick fix (safe cleanup routine)
# 1. Look before you delete
docker system df
# 2. Clear only what you are sure about: stopped containers + dangling images + unused networks
docker system prune -f
# 3. Volumes are a separate axis; list, confirm, then prune (-a is what touches named volumes)
docker volume ls -f dangling=true # list "unused" volumes first, confirm nothing to keep
docker volume prune -a -f # or scope: --filter label=my-project
# 4. Build cache is always recyclable
docker builder prune -f
# Isolation on a shared daemon: label everything, clean with filters
docker run --label my-project=true ...
docker volume create --label my-project=true ...
docker system prune -f --filter label=my-project=true
What you will run into
Disk is tight and you want to clean Docker: docker system prune -f finishes but little space comes back; docker volume prune reports Total reclaimed space: 0B;
or the opposite — you follow a "cleanup tutorial" and images/volumes you are still using get deleted, and services fail to start.
Each prune-family command has its own default scope, and some defaults are counter-intuitive (verified: docker volume prune by default deletes only unused anonymous volumes; named volumes need -a).
Why this happens
The prune family recycles by category; every command has its own default scope, and -a/--volumes/--filter widen or narrow it:
| Command | Deletes | Notes |
|---|---|---|
docker system prune | stopped containers + dangling images + unused networks | touches no volumes, no tagged-but-unused images, no build cache |
docker system prune -a | + all unused images (including tagged) | dangerous: removes cached images that are not currently used |
docker system prune --volumes | + unused anonymous volumes | does not delete named volumes (help: "Prune anonymous volumes") |
docker volume prune | unused anonymous volumes | named volumes are not deleted by default (verified; see help text below) |
docker volume prune -a | all unused volumes (including named) | --filter label= narrows precisely |
docker container prune | stopped containers | --filter label= / until= |
docker image prune | dangling images | -a also removes unused tagged images |
docker network prune | unused networks | --filter label= |
docker builder prune | build cache | safe to recycle (only slows the next build) |
Three defaults bite the most, all verified: ① system prune deletes stopped containers by default — you think you are clearing cache, but every docker create/exited container is gone, including ones you planned to docker start again;
② volumes are "not touched by default": system prune without --volumes deletes no volumes at all, while volume prune by default deletes only anonymous volumes (--help verbatim: -a, --all Remove all unused volumes, not just anonymous ones;
reproduced: named volume survives a plain volume prune) — two opposite defaults, mixing them up is a guaranteed trap; ③ dangling images = old images with no tag (<none>:<none>) that a newer build replaced;
image prune clears only those by default, tagged-but-unused images need -a (reproduced: prune-demo-img:latest survives system prune).
Deterministic takeaway: run docker system df before deleting; on a shared daemon, tag everything with --label and scope cleanups with --filter label=...; never run an unfiltered system prune -a on shared state.
Step by step
1. See where the space went — Must do
docker system df
# Images / Containers / Local Volumes / Build Cache — look before you delete
2. Containers — By diagnosis
Stopped containers: docker container prune -f; to scope precisely: docker container prune -f --filter label=my-project=true (reproduced: only matching containers removed).
3. Images — By diagnosis
docker image prune -f clears dangling only; -a also removes unused tagged images (reproduced: default system prune removed the dangling image but kept prune-demo-img:latest; docker image rm <tag> removes tagged ones).
No unfiltered -a on shared state.
4. Volumes — Must do (easiest to delete or to miss)
docker volume ls -f dangling=true # unused volume list; confirm manually first
docker volume prune -a -f # default only deletes anonymous! -a includes named
Reproduced: docker volume prune -f deleted only the anonymous volume; prune-demo-named survived; docker volume prune -a -f --filter label=prune-demo=true deleted it.
5. Build cache — Optional
docker builder prune -f: only slows the next build; does not affect images/containers.
How to confirm the cleanup was safe
After pruning, docker ps -a, docker images, docker volume ls still show everything you need; docker compose up starts fine (volume data and tagged images are still there).
Reproduced: default system prune kept the tagged image and the named volume; only stopped containers/dangling images/unused networks went away.
Other environments
| Command/flag | Verified behavior (docker 29.1.3) |
|---|---|
system prune (default) | Deletes stopped containers + dangling images + unused networks; keeps volumes/tagged images/build cache |
system prune -a | Adds all unused images (including tagged) |
system prune --volumes | Adds unused anonymous volumes (help text) |
volume prune (default) | Deletes unused anonymous volumes only (-a help: "not just anonymous ones") |
volume prune -a | Deletes all unused volumes (including named) |
--filter label= | Supported for containers/volumes/networks/images; precise scoping (verified) |
Symptom
# Cleanup did not free space (volumes untouched)
$ docker volume prune -f
Total reclaimed space: 0B # named volumes are not in the default scope — that is why
# Cleanup tutorial backfired: images in use were deleted
$ docker system prune -a -f # all unused tagged images gone -> next start re-pulls
Signature: space unchanged after prune (volumes/tagged images not covered by defaults); or blowback (unfiltered -a deleted shared images).
Cause
Prune-family commands have different default scopes: system prune includes stopped containers and dangling images but no volumes; volume prune deletes only unused anonymous volumes by default; tagged-but-unused images need -a. Unfiltered -a/--volumes on a shared daemon deletes things other people need.
Solution
docker system dfto see the four categories first;- Pick the command by target: containers=
container prune, dangling images=image prune, volumes=volume prune -a(after confirming), cache=builder prune; - On shared environments, label everything and scope cleanups with
--filter label=; - Re-check with
docker ps -a/docker images/docker volume lsafter deleting.
Verification Evidence
- Local reproduction (2026-08-31, custom prebuilt WSL, docker 29.1.3; all demo artifacts labeled
prune-demo=true, cleanups scoped with--filter label=, no unfiltered system prune): - Built artifacts: dangling image (two builds) + 2 stopped containers (one with an anonymous volume) + named volume + network;
system prune -f --filter label=prune-demo=true: deleted 2 containers + 1 network + 1 dangling image (untagged: e71931f35c62); taggedprune-demo-img:latestand volumeprune-demo-namedkept;volume prune -f: deleted only the anonymous volume (25b060c64c02…); named volume kept;volume prune -a -f --filter label=prune-demo=true: deleted the named volume;volume prune --helpverbatim:-a, --all Remove all unused volumes, not just anonymous ones;builder prune -f: local build cache empty, 0B (semantics verified).
Evidence details kept locally (2026-08-31)
Related canonicals
Read in order: precondition -> same root / same domain / related:
- Related (similar errors, different cause):
- WSL reports No space left but df has space (same "out of space" family: WSL vhdx vs Docker usage)
- docker compose stack fails to start: dependency not ready (same-batch Docker domain reproduction)
Applicable tools (1)
This problem is linked from the tool pages below — reachable from either side.
dockerSource & evidence
← Coding Agents & Terminal domain | Problem Catalog Experience | Facing another issue? Go to the Helpdesk