Docker fills up your disk. Pulled images stack up, exited containers stick around, stale volumes from old projects sit there forever, and BuildKit's cache grows independently of everything else. The cleanup commands are simple; the flags decide which categories of stuff get wiped and how aggressive the sweep is.
The everyday cleanup
docker system pruneThat removes:
- All stopped containers.
- All unused networks (networks with no containers attached).
- All dangling images (untagged layers — the leftovers from rebuilding an image).
- All build cache that isn't referenced by an in-use image.
It does not remove tagged images you have on disk but aren't currently using, and it does not touch named volumes. That's the safe default — won't delete data, will reclaim a few GB on a typical dev machine.
# Skip the "are you sure?" prompt
docker system prune -fGoing further
docker system prune -aThe -a flag adds all unused images to the sweep — not just dangling ones. An image is "unused" if no container is using it. If you keep many tagged images around for testing, this can free a lot of space. It also means the next time you need one of those images, Docker re-pulls it.
docker system prune -a --volumesThis is the nuclear option. Adds unused named volumes to everything else. Volumes are where state lives — mysql-data, pg-data, wp-content. Use with care. Docker asks for confirmation, but past that, the data is gone.
I run docker system prune casually, docker system prune -a when I want to free more, and docker system prune -a --volumes only when I'm sure I have no important state in named volumes.
Per-resource prune commands
Sometimes you want to clean just one category:
docker container prune # stopped containers only
docker image prune # dangling images only
docker image prune -a # all unused images
docker volume prune # unused volumes (data loss potential)
docker network prune # unused networks
docker builder prune # BuildKit cache only
docker builder prune -a # all BuildKit cache, including used layersdocker builder prune is the one most people forget. The BuildKit cache lives separately from docker system prune's sweep until very recent Docker releases, and it can quietly grow to tens of GB on a host that does a lot of building.
Checking how much Docker is actually using
docker system dfOutput looks something like:
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 47 12 18.3GB 14.1GB (77%)
Containers 18 3 245MB 201MB (82%)
Local Volumes 34 8 8.2GB 6.1GB (74%)
Build Cache 412 0 12.4GB 12.4GB
That tells you:
- 47 images on disk taking 18.3 GB; 14.1 GB of that is reclaimable (
docker system prune -awould get it). - 18 containers but only 3 running; 201 MB of stopped containers (
docker container prunewould clean these). - 34 volumes, 8 of them attached to running containers; 6.1 GB of orphaned volume data.
- Build cache: 12.4 GB, all reclaimable with
docker builder prune.
Always run docker system df before pruning so you know what you're freeing. The -v (verbose) flag shows the per-item breakdown.
Docker Desktop's disk image (Mac and Windows)
On Mac and Windows, Docker Desktop runs Linux in a VM, and everything Docker stores — images, volumes, containers, BuildKit cache — lives inside a single virtual disk image on the host. The image grows but doesn't shrink automatically. So even after docker system prune -a, the host disk usage doesn't drop. The internal Docker filesystem has free space; the wrapper file is still huge.
To reclaim the host space:
- Docker Desktop → Settings → Resources → Advanced → Disk image size — drag the slider down. Docker compacts the underlying virtual disk.
- Or
Troubleshoot → Clean / Purge data— nukes everything Docker has stored and recreates a fresh disk. Lose everything Docker had.
Linux is simpler — pruning frees disk immediately because there's no wrapper VM. Full picture in Where Are Docker's Files on a Mac?.
Filters for selective cleanup
prune supports --filter for targeted sweeps:
# Remove images older than 24 hours
docker image prune -a --filter "until=24h"
# Remove images NOT marked with this label
docker image prune -a --filter "label!=keep"
# Remove stopped containers older than a week
docker container prune --filter "until=168h"Useful in CI to keep build hosts clean: a nightly docker system prune -a --filter "until=24h" keeps a week-or-younger image cache without wiping everything.
Automating cleanup
A cron entry that runs daily:
0 3 * * * /usr/bin/docker system prune -af --filter "until=168h" >> /var/log/docker-prune.log 2>&1That runs at 3 AM, removes everything unused that's older than a week, and logs. Adjust the threshold for your build cache hit rate.
Common pitfalls
docker system prune -a --volumeson a database host. All named volumes that aren't currently mounted die. If you stopped your database container "just for a minute," that volume is now in the kill list.- Pruning while a build is running. BuildKit references its cache layers; pruning during a build can cause weird failures. Wait for builds to finish.
- Surprised by Mac/Windows disk usage not dropping after prune. It dropped inside Docker's VM, not on your Mac. Use Docker Desktop's "Disk image size" slider to compact, or "Clean / Purge data" to reset.
docker builder pruneclearing too much. Without-a, it leaves recent/used cache alone. With-a, every layer goes — next build is from scratch.- Volumes lingering after a
docker compose down -v. That command removes the project's named volumes, but if you mounted external volumes (declared indocker-compose.ymlasexternal: true), they survive. Verify withdocker volume ls.
What to do next
- Docker Volumes vs Bind Mounts — the persistence story behind
--volumes. - Where Are Docker's Files on a Mac? — why Mac disk usage doesn't drop after pruning.
- Docker Cheat Sheet — all the prune commands in one place.
FAQ
Sources
Authoritative references this article was fact-checked against.
- docker system prune — official referencedocs.docker.com
- BuildKit cache garbage collectiondocs.docker.com

