Where Docker disk space actually goes

Images, build cache, stopped containers and volumes each cost you differently to reclaim. What each one is, which are safe to delete, and the one flag that destroys data.

Updated 2026-09-20

Short answer: run docker system df. It splits what Docker holds into four rows, and the four are not equally safe to reclaim: images, containers, local volumes and build cache. Three of them come back by pulling or rebuilding. The fourth is the only copy of whatever is inside it.

The four rows, safest first

  • Build cache. Layers BuildKit kept so your next build starts warm. Deleting it costs exactly one cold build per project. docker builder prune -a -f clears it.
  • Unused images. Base images and old tags nothing references. They come back from the registry when something pulls them, at the cost of the download. docker image prune -a -f takes every image no container uses.
  • Stopped containers. A stopped container keeps its writable layer. Anything a process wrote inside the container, and not into a mounted volume, lives in that layer and goes with it. If that describes data you care about, it was in the wrong place, and the time to move it is before the prune. docker container prune -f removes stopped containers.
  • Volumes. A named volume is where databases, message queues and anything else with real state keep it. There is no registry behind a volume and no rebuild that brings one back. This row is not a cache and should never be cleared like one.

The flag that turns cleanup into data loss

docker system prune --volumes deletes every volume no container currently references. "Currently" is the trap: a database whose container is stopped, or removed and recreated on demand by compose, counts as unreferenced at that moment. People run the flag to reclaim cache space and delete a local database as a side effect. If you want volume space back, list them with docker volume ls, look at what each one is, and remove the ones you can name with docker volume rm, one at a time.

Why the numbers look strange

Two things about Docker's accounting are worth knowing before you compare numbers. Layers are shared, so deleting three images can free far less than the sum of their listed sizes. And on Docker Desktop the whole store lives inside a virtual machine disk the host cannot see into, so the honest measurement is the engine's own system df, before and after, rather than anything a directory walk on the host can tell you.

Doing it with a tool that keeps score

dev-prune treats container disk as something you look at often and delete from deliberately. devp caches docker prints the four rows, sized, with how much the engine says is reclaimable, and deletes nothing. devp caches clear docker runs the three narrow commands above (build cache, unused images, stopped containers), prints them before running anything, asks, and measures what came back by asking the engine again afterwards, so the figure lands in devp stats instead of being forgotten. Podman, nerdctl, finch and Apple's container engine each get the same report and their own engine's equivalent of those narrow commands.

Volumes are excluded from that estimate and from those commands: none of the engine commands dev-prune runs contains the word "volume", and a test fails the build if one appears. The one path that touches them, devp caches clear docker --include-volumes, lists the unused volumes by name and takes each deletion as a typed pick at a real terminal, one unforced docker volume rm per pick. It refuses --yes, --json and piped input, so no script, scheduler or AI agent can reach the picking. And typed cold, it does not delete at all: the pick list arms only within ten minutes of a completed dry run for that engine, so the real command runs the dry run instead and says so. Nothing bulk, nothing silent.

Common questions

Is docker system prune safe to run?

Without --volumes, mostly: it removes stopped containers, unused networks, dangling images and dangling build cache, all of which come back by pulling or rebuilding. The caveat is stopped containers, whose writable layers go with them, so anything a process wrote inside a container rather than into a volume is lost. With --volumes it stops being a cleanup command: it deletes every volume no container currently references, which includes the database whose container happens to be stopped.

What is using all my Docker disk space?

Run docker system df for the split across images, containers, volumes and build cache, or devp caches docker for the same figures with per-row reclaimable amounts. On build machines the build cache is usually the biggest recoverable row; on machines running databases in containers, volumes often dominate and should be left alone.

How do I delete Docker volumes safely?

By name, one at a time, after looking: docker volume ls, then docker volume rm for the ones you can identify. Avoid docker volume prune and docker system prune --volumes, which delete by reference counting rather than by your judgment. devp caches clear docker --include-volumes wraps the same per-name deletion in a typed pick list that scripts and agents cannot answer.