Skip to content
Go back

Cleaning Up Docker Disk Space the Right Way

By SumGuy 5 min read
Cleaning Up Docker Disk Space the Right Way

Your Docker host is running out of disk space. You’ve got dangling images, stopped containers, unused volumes, and 500GB of build cache nobody needs. You could delete it all with docker system prune -a, but that nuclear option will delete more than you intended.

Let’s be surgical about this.

Understand What’s Eating Space

First, check what’s using disk:

Terminal window
$ docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 42 8 18.5GB 14.2GB
Containers 89 3 2.1GB 2.0GB
Local Volumes 12 2 500MB 400MB
Build Cache - - 12.3GB 9.8GB

This tells you:

The build cache is usually the biggest culprit.

The Safe Cleanup Path

Step 1: Stop and Remove Unused Containers

Stopped containers take up space. Remove them:

Terminal window
# See stopped containers
$ docker ps -a --filter "status=exited"
# Remove all stopped containers
$ docker container prune -f
Deleted Containers:
abc123def456
def456ghi789
...
Deleted Networks:
old_network_1
old_network_2

This is safe. Stopped containers aren’t running anything.

Step 2: Remove Dangling Images

Dangling images are layers that have no tag and aren’t used by any container. They’re orphaned:

Terminal window
# See dangling images
$ docker images --filter "dangling=true"
REPOSITORY TAG IMAGE ID CREATED SIZE
<none> <none> abc123def456 3 weeks ago 850MB
<none> <none> def456ghi789 2 weeks ago 1.2GB
# Remove them
$ docker image prune -f
Deleted Images:
deleted sha256:abc123def456
deleted sha256:def456ghi789
Space reclaimed: 2.1GB

Safe. These images aren’t tagged and nothing uses them.

Step 3: Clean Up Build Cache Selectively

Build cache accumulates fast, especially if you change Dockerfiles frequently. You can clean it smartly:

Terminal window
# See detailed cache usage
$ docker builder du
ID: abc123def456 BUILD ID: cache1 CACHED SIZE: 500MB
ID: def456ghi789 BUILD ID: cache2 CACHED SIZE: 300MB
...
# Prune cache (keeps recent builds, removes old)
$ docker builder prune -f
Space reclaimed: 8.5GB

Or be more aggressive:

Terminal window
# Remove all cache (nuke it)
$ docker builder prune -a -f
Space reclaimed: 12.3GB # But next build will be slow

Step 4: Remove Unused Volumes

Volumes persist data. Be careful here—deleting a volume deletes data:

Terminal window
# See unused volumes (not mounted by any container)
$ docker volume ls --filter "dangling=true"
DRIVER VOLUME NAME
local olddb_data_1
local test_volume_2
# Remove them
$ docker volume prune -f
Deleted Volumes:
olddb_data_1
test_volume_2
Space reclaimed: 450MB

Only delete volumes you don’t need. If in doubt, leave them.

Step 5: Remove Unused Images (Carefully)

This is where you can accidentally delete images you wanted to keep. Be selective:

Terminal window
# See all images
$ docker images
REPOSITORY TAG IMAGE ID SIZE
ubuntu 22.04 abc123def456 77MB
myapp latest def456ghi789 500MB
node 18 ghi789jkl012 900MB
old-python 3.8 jkl012mno345 800MB
# Remove a specific old image
$ docker rmi old-python:3.8
Untagged: old-python:3.8
Deleted: jkl012mno345
# DON'T do this (removes all unused images):
# $ docker image prune -a # ← This deletes even images you might use

The Nuclear Option: docker system prune

docker system prune is convenient but aggressive:

Terminal window
# Remove stopped containers, dangling images, unused networks
$ docker system prune -f
Deleted Containers:
abc123def456
...
Deleted Images:
def456ghi789
...
Deleted Networks:
old_network
Space reclaimed: 3.2GB

Add -a and it gets nuclear:

Terminal window
# DANGER: Remove everything not in use
$ docker system prune -a -f
# Deletes: stopped containers, all unused images, unused volumes, networks
# You may delete images you wanted to keep

Use --filter to be more selective:

Terminal window
# Only prune things older than 72 hours
$ docker system prune --filter "until=72h" -f

Prevention: Limit What Docker Stores

If you want to avoid the cleanup dance, set limits in /etc/docker/daemon.json:

/etc/docker/daemon.json
{
"storage-driver": "overlay2",
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
},
"build-cache": {
"gc": {
"policy": [{"all": false, "unused-for": 1209600}]
}
}
}

Restart Docker:

Terminal window
$ sudo systemctl restart docker

Now:

Safe Cleanup Script

Here’s a script that cleans up safely without deleting important stuff:

cleanup.sh
#!/bin/bash
set -e
echo "=== Docker Cleanup ==="
echo "1. Removing stopped containers..."
docker container prune -f
echo "2. Removing dangling images..."
docker image prune -f
echo "3. Pruning build cache (keeps recent)..."
docker builder prune -f
echo "4. Removing unused volumes..."
docker volume prune -f
echo "5. Current disk usage:"
docker system df
echo "=== Done ==="

Run it:

Terminal window
$ chmod +x cleanup.sh
$ ./cleanup.sh

Checklist Before Big Cleanup

What to Do Every Week

Terminal window
# Weekly cleanup (safe)
$ docker container prune -f
$ docker image prune -f
$ docker volume prune -f
$ docker system df

This takes 30 seconds and prevents disk bloat. Better than waiting until you’re at 100% capacity.

Docker cleanup isn’t scary if you understand what each command does. Remove stopped containers and dangling images without hesitation. Be careful with volumes. Don’t nuke build cache unless you’re really constrained. And set log limits to avoid surprises.


Share this post on:

Send a Webmention

Written about this post on your own site? Send a webmention and it may appear here.


Previous Post
ss Is the New netstat (And It's Better)
Next Post
Why Your Cron Job Is Failing Silently

Related Posts