How to Clean Up Docker Disk Usage Safely
Safe Docker disk usage cleanup means auditing first with docker system df, then removing only what you know is unused: stopped containers, dangling images, and orphaned build cache. Never run a blanket docker system prune -a --volumes on a production host unless you have confirmed no named volume holds data you still need. Volumes are where Docker silently eats disk, and they are also where your database lives.
A Docker host fills up gradually and then all at once. Builds pile up layers, old images stick around after a docker pull, exited containers keep their writable layer, and logs grow without limit. One day a deploy fails with no space left on device and you are SSH'd in at midnight trying to figure out what is safe to delete. This guide walks through doing that cleanup without taking down a running service.
Start by finding out what is actually using space
Before you delete anything, look. Run this on the host:
docker system df
You get a summary broken into Images, Containers, Local Volumes, and Build Cache, with a RECLAIMABLE column telling you how much of each is safe to free. That column is the honest one. If Images shows 40GB with 30GB reclaimable, you have a lot of dead weight. If Local Volumes shows 200GB with 0GB reclaimable, that space is in use and pruning it would destroy data.
For a per-object breakdown, add -v:
docker system df -v
This lists every image, container, and volume individually with its size. It is verbose, but it is how you find the one 12GB volume nobody remembered mounting. Pipe it through less on a busy host.
To see what a running container is doing to the disk from the outside, check the host directly:
du -sh /var/lib/docker/*
The usual offenders are overlay2 (image and container layers), volumes, and containers (which holds logs). If containers is huge, your problem is log files, not images.
Prune the safe stuff first
Start with the low-risk removals. Each of these has a targeted command so you are not guessing.
Stopped containers. An exited container still owns its writable layer. Remove all stopped ones:
docker container prune
Dangling images. These are untagged layers left behind when you rebuild an image with the same tag. The old <none>:<none> image is orphaned. Clear them:
docker image prune
Build cache. BuildKit caches every layer, and it grows fast on a CI host. Reclaim it:
docker builder prune
Each prune command asks for confirmation and prints exactly how much it freed. Read the total before you type y. If you want to see what would go without committing, most prune commands respect --filter so you can scope by age:
docker image prune -a --filter "until=168h"
That removes unused images older than a week and keeps anything recent. The -a flag widens image prune from dangling-only to all unused images, which is the flag that actually reclaims serious space. Just know that "unused" means "not referenced by any container," so an image you pulled but have not run yet will go too.
The dangerous command, and how to use it anyway
You have seen docker system prune -a --volumes recommended everywhere. It works, and it is the fastest way to lose a database. Here is what each part does:
docker system pruneremoves stopped containers, unused networks, dangling images, and build cache.- Adding
-aalso removes all images not used by a running container. - Adding
--volumesalso removes volumes not attached to any container.
That last flag is the trap. A named volume holding your Postgres data is "not attached to a container" the moment the container is stopped for a deploy. Prune with --volumes in that window and the data is gone. There is no undo.
So the rule is simple. On a dev machine you are wiping anyway, docker system prune -a --volumes is fine. On anything with real data, drop --volumes and clean volumes by hand instead:
docker volume ls
docker volume ls -f dangling=true
docker volume rm <specific-volume-name>
List them, look at each one, and remove named ones individually only after you are sure. The dangling=true filter shows volumes with no container attached, which is a starting point for review, not an auto-delete list.
Logs are the quiet disk killer
A chatty container with default logging will write an unbounded JSON log file until the disk is full. Check the size:
du -sh /var/lib/docker/containers/*/*-json.log
You cannot safely rm these while the container runs, but you can truncate one in place:
truncate -s 0 /var/lib/docker/containers/<container-id>/<container-id>-json.log
The real fix is to cap logging so it never happens again. Set limits in /etc/docker/daemon.json:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
Then sudo systemctl restart docker. New containers rotate logs at 10MB and keep three files. Existing containers keep their old config until recreated, so redeploy them to pick up the limit. This one change prevents most "disk full" pages I have been woken up for.
Make Docker disk usage cleanup a weekly routine
Cleanup you only do during an outage is cleanup you do badly. A weekly cron on each host handles the safe removals without touching volumes:
0 4 * * 0 docker system prune -af --filter "until=168h"
Note there is no --volumes here on purpose. This runs at 4am Sunday and clears images and cache older than a week. Keep the volume review manual.
The harder part is knowing a host is trending toward full before it gets there. Watching docker system df output over SSH across a fleet does not scale past a couple of servers. This is where Docker HQ helps: it monitors host disk, CPU, and memory across every server you manage and sends a push alert when disk crosses a threshold, so you clean up on a Tuesday afternoon instead of at midnight. You can run the prune commands over SSH from your phone in the same session. If you are running more than one or two hosts, the pricing page covers multi-host and team access.
Disk pressure is usually a symptom of something else running hot, so it pairs with real monitoring. Our complete guide to Docker container monitoring covers the metrics worth watching, and if you want to catch failures the moment they happen, see how to get alerted when a Docker container goes down. Teams running shared infrastructure should also read Docker uptime monitoring for small teams.
Frequently asked questions
Does docker system prune delete my volumes?
No, not by default. Plain docker system prune and docker system prune -a leave volumes alone. Volumes are only removed if you add the --volumes flag. That flag deletes any volume not currently attached to a container, which includes named volumes whose container is stopped, so treat it as destructive.
How do I clean up Docker without stopping running containers?
All the prune commands skip anything a running container depends on. docker container prune only removes stopped containers, docker image prune skips images in use, and docker volume prune skips attached volumes. You can run them on a live host safely. The only manual care needed is with named volumes and log truncation.
What is Docker build cache and is it safe to delete?
Build cache is the set of intermediate layers BuildKit stores to speed up future builds. Deleting it with docker builder prune is safe. Your next build just runs slower because it rebuilds those layers from scratch. On CI hosts the cache is often the single largest reclaimable item.
Why is /var/lib/docker so large even after pruning?
Usually because the space is in named volumes, which prune leaves alone, or in active container logs. Run docker system df -v to see volume sizes and du -sh /var/lib/docker/* to find whether the weight is in volumes, overlay2, or containers. Log files under containers are a common surprise.
Start with docker system df on your fullest host right now and read the reclaimable column before you touch anything. That single command tells you whether you have an image problem, a volume problem, or a log problem, and each one has a different safe fix.