Pruning and Disk Management
A long-running Docker host fills its disk quietly. Stopped containers keep their writable layers, every rebuild of driftwood/web leaves the old image dangling, unused volumes hold abandoned data, and the BuildKit cache grows with every build — none of it reclaimed automatically. The space goes somewhere, and one day the host is at 100% on /var/lib/docker with no obvious culprit.
docker system df shows where the space went, and the prune family reclaims it. The danger is that the aggressive flags — prune -a and --volumes — will also delete things you meant to keep, including data. There is no undo on a prune, so the difference between routine cleanup and an outage is knowing exactly which flag crosses which line.
What Silently Eats Disk
Four accumulators grow without bound on a busy host. Stopped containers keep their writable layers until they are removed — a container you stopped last week still occupies disk. Dangling images are untagged layers orphaned when a tag moves to a rebuild: every docker build -t driftwood/web leaves the previous image's layers behind, now nameless. Unused volumes are named or anonymous volumes no container references anymore. And the build cache is BuildKit's pile of intermediate layers kept across builds. On a host that rebuilds and restarts driftwood/web daily, all four climb steadily, and only the build cache is at all obvious.
docker system df — Accounting the Space
docker system df summarizes images, containers, volumes, and build cache with a total size and a RECLAIMABLE column that tells you how much you could get back. The summary is the headline; the itemized view is what you act on:
docker system df docker system df -v
-v lists every image, container, and volume individually, so you see exactly which driftwood/web tag or which volume is holding the gigabytes. Deleting from that knowledge is a different act than running an aggressive prune and hoping.
The Targeted Prunes
Each accumulator has a prune that touches only it. docker container prune removes stopped containers. docker image prune removes dangling images — and -a removes all unused images, a much bigger cut. docker volume prune removes unused volumes. docker builder prune reclaims the build cache. Running the specific one keeps the blast radius small: if the build cache is what system df shows as the largest consumer, docker builder prune reclaims it without going near images or volumes.
docker system prune — The Sweep
docker system prune does several of those at once — it removes stopped containers, dangling images, unused networks, and (with newer defaults) the build cache, in a single command:
# safe to schedule: dangling images, stopped containers, networks, cache docker system prune # dangerous: also every unused image AND every unused volume docker system prune -a --volumes
By default the plain form does not touch volumes or tagged-but-unused images. That is the deliberate safety line, and it is exactly the line that -a and --volumes cross.
docker system pruneprune -a --volumesThe -a and --volumes Danger
docker system prune -a deletes every image not currently used by a container — including base images you will need offline (python:3.12-slim, postgres:16) and the previous driftwood/web tag you were keeping for a rollback. --volumes deletes unused volumes, and here is the trap, which is really about order: docker system prune removes stopped containers first. A stopped container normally protects its volume — docker volume prune alone would leave driftwood-db-data untouched. But the sweep deletes the stopped db container, which orphans the volume, and the --volumes step then deletes the now-unreferenced driftwood-db-data, destroying the database (this is the data from Chapter 6). There is no undo. The aggressive prune is a hand tool for a moment when you have already confirmed what it will take.
Scheduling It Safely
The single-host pattern for unattended reclamation is a cron'd targeted prune with an age filter, not an unguarded sweep. Run docker image prune and docker builder prune with a --filter until= window so only things older than, say, a week are removed:
docker image prune -a --filter "until=168h" --force docker builder prune --filter "until=168h" --force
Notice what is absent: no --volumes, no plain system prune -a without an age window. The schedule reclaims old, unused images and stale cache, and it never goes near a volume — so a stopped db container can never cost you driftwood-db-data at 3am.
docker system prune — removes stopped containers, dangling images, unused networks, and build cache. Safe to schedule: it leaves tagged images and all volumes alone, so it can run unattended without ever destroying data or a rollback image.
docker system prune -a --volumes — additionally deletes every image not in use by a running container and every unused volume. It reclaims far more, but it will take out rollback images and, because it removes stopped containers first, the volume of a merely-stopped container too — including driftwood-db-data. Run it only by hand, after docker system df -v, never on a schedule.
- Running
docker system prune -a --volumesto "clean up" while thedbcontainer is stopped — the sweep removes the stopped container first, orphaningdriftwood-db-data, then deletes the now-unused volume; the database is gone, with no recovery. - Using
-aon a host with limited connectivity and deleting base images such aspython:3.12-slimorpostgres:16that aren't currently running — the next build or start has to re-pull and fails if the registry is unreachable. - Letting the build cache grow unbounded on a CI-adjacent host that rebuilds
driftwood/webconstantly —docker builder pruneis the only thing that reclaims it, andsystem dfshows it as the largest consumer when nobody runs it. - Assuming
docker rmof a container also removes its anonymous volumes — it does not unless-vis passed, so abandoned anonymous volumes accumulate invisibly untilvolume prunefinds them. - Scheduling an aggressive prune without an age filter, so a freshly stopped container or a just-built image gets deleted within minutes of being created, mid-workflow.
- Run
docker system df— and-vfor the itemized view — before any prune, so you delete from knowledge rather than reflex. - Schedule a targeted, age-filtered prune (
image pruneplusbuilder prune --filter until=168h) on long-lived hosts to reclaim space unattended without touching live data. - Never put
--volumesor-ain an automated prune — reserve those for interactive cleanup after confirming nothing live depends on what they would remove. - Keep durable data in named volumes you can identify and exclude, so a volume cleanup can distinguish
driftwood-db-datafrom genuinely abandoned anonymous volumes.
system df / system prune / image prune family
Registry garbage collection reclaims disk on the registry rather than the host
Kubernetes the kubelet's image garbage collection instead of a host-level prune
Knowledge Check
Which four things silently consume disk on a long-running Docker host?
- Stopped containers, dangling images, unused volumes, and the build cache
- Running containers' memory, swap files, the daemon log, and DNS caches
- User-defined networks, firewall rules, port mappings, and the embedded DNS table
- Healthcheck logs, the event stream history, restart counters, and inspect output
Why is plain docker system prune safe to schedule while docker system prune -a --volumes is not?
- The plain form leaves tagged images and all volumes alone;
-a --volumesdeletes unused images and volumes, including a stopped container's data volume - The plain form removes nothing at all from disk and merely prints a harmless dry-run list of everything the more aggressive
-a --volumesform would delete - The plain form always prompts interactively for confirmation before deleting, while
-a --volumescan never be made to prompt - Only the
-a --volumesform ever deletes live data out from under currently running containers on the host
You run docker system prune -a --volumes while the db container is stopped. What is the consequence?
- The sweep removes the stopped container first, orphaning
driftwood-db-data, which is then deleted — destroying the database with no recovery - Nothing happens to the volume at all, because the stopped
dbcontainer still counts as a user that protects it - Docker snapshots the volume to a safe backup first, so the database can be fully restored afterward
- The prune first starts the
dbcontainer so the volume registers as in use and is therefore spared
After docker rm web, you find an anonymous volume the container created still on disk. Why?
docker rmdoes not remove anonymous volumes unless-vis passed, so they linger until a volume prunedocker rmonly stops and unregisters the container and never deletes any of its on-disk storagedocker rmautomatically converts the anonymous volume into a named one to keep it safe- The volume is still actively mounted by another running container, so
rmwas not permitted to remove it
You got correct