July 12, 2026 · Docker HQ Team

How to Prune Docker Images Without Breaking Things

To prune Docker images safely, start with docker image prune (dangling layers only), check what will be freed, and only reach for docker image prune -a once you understand it deletes every image no running container references. Add --filter "until=168h" to spare recent images, and never run a bare docker system prune -a --volumes on a host you care about. That last one takes your volumes with it.

Disk fills up on Docker hosts. It always does. You build a few images a day, pull updates, and six months later /var/lib/docker is 40GB of layers you forgot about. Pruning is how you get that space back. The catch is that some of those commands delete more than you think, and the difference between a safe cleanup and a bad afternoon is a couple of flags.

What actually eats your disk

Before deleting anything, look at where the space went:

docker system df

You get a breakdown by images, containers, local volumes, and build cache. The RECLAIMABLE column tells you how much you can free without touching anything in active use. For more detail, add -v:

docker system df -v

This lists every image with its size and how many containers reference it. An image with 0 in the containers column is a candidate for removal. An image backing a running container is not, and Docker won't let you delete it anyway unless you force it.

Build cache is often the real culprit. If you build images in CI or locally on the host, the cache grows without bound until you clear it. docker system df will show it as a separate line, and it can dwarf your actual images.

Dangling images: the safe first pass

Dangling images are layers with no tag and no repository, usually leftovers from a rebuild where the latest tag moved to a newer image. They're safe to delete because nothing references them by name.

docker image prune

This removes only dangling images. Docker asks for confirmation first. Run it, say yes, and you'll usually reclaim a chunk of space with zero risk. If you want to see the dangling images before you commit:

docker images --filter "dangling=true"

This is the command you can run on a schedule and forget about. It never touches a tagged image.

The dangerous one: prune -a

Here's where people get burned:

docker image prune -a

The -a flag removes all images not currently used by a container. Not just dangling ones. If you have a tagged image that no container is running right now, it's gone. That includes base images you pulled for a build that finished, images for a service you stopped for maintenance, and anything you were keeping around "just in case."

The problem is the phrase "not used by a container." A stopped container still counts as using its image. But if you pruned your stopped containers first (or run everything through docker run --rm), those images now look unused. Prune -a will happily delete an image you plan to start again in ten minutes, and then you're re-pulling a 900MB image over a slow link on a production box.

Before you run it, see what would go. There's no true --dry-run on image prune, so list unused images explicitly:

docker images --filter "dangling=false" --format '{{.Repository}}:{{.Tag}} {{.Size}}'

Then cross-check against what your containers actually use. On a host with more than a couple of services this gets tedious fast, which is exactly why the filter below matters.

Use filters so you don't overreach

The until filter restricts pruning to images older than a cutoff. This is the single most useful safety habit. Keep anything recent, clean up the ancient stuff:

# Remove unused images older than 7 days
docker image prune -a --filter "until=168h"

The value is a Go duration (h for hours) or a timestamp. Seven days is a reasonable default. Anything you touched this week survives, so a service you stopped yesterday keeps its image.

You can also filter by label, which is the right approach if you tag images with a project or environment label at build time:

docker image prune -a --filter "label=environment=staging"

Now a cleanup on your staging pipeline can't reach production images even if you fat-finger the host.

Cleaning build cache separately

Build cache has its own prune command, and treating it separately keeps your image cleanup honest:

# Everything
docker builder prune

# Only cache older than 7 days
docker builder prune --filter "until=168h"

# Free-space target: keep at most 10GB
docker builder prune --keep-storage 10gb

On a build host, docker builder prune --keep-storage on a cron is usually all the cache management you need. It trims down to your ceiling and leaves the recent layers that make your next build fast.

The command that deletes your volumes

You'll see docker system prune recommended everywhere. Understand what each version does before you paste it into a root shell.

# Dangling images, stopped containers, unused networks, build cache
docker system prune

# Also every unused image (the -a trap, host-wide)
docker system prune -a

# Also unused volumes -- this deletes DATA
docker system prune -a --volumes

That last flag is the one to fear. Volumes hold your database files, your uploaded content, anything persistent. An "unused" volume just means no running container has it mounted right now. Stop your Postgres container for maintenance, run docker system prune --volumes, and you may have just deleted the database. Docker will ask for confirmation, but at 3am confirmation prompts get muscle-memory y.

Rule of thumb: never combine --volumes with prune on a host that stores real data. If you need to clean volumes, list them and remove them by name deliberately with docker volume ls and docker volume rm.

A safe routine to prune Docker images

Here's a sequence that reclaims most of your space without risking anything in use:

# 1. See the state of things
docker system df

# 2. Drop dangling images (always safe)
docker image prune -f

# 3. Trim build cache to a ceiling
docker builder prune --keep-storage 10gb -f

# 4. Remove unused images older than a week
docker image prune -a --filter "until=168h" -f

# 5. Confirm what you got back
docker system df

The -f skips the confirmation prompt, which you only want once you trust the commands. Steps 2 and 3 are safe to automate on any host. Step 4 is safe as long as your until window is longer than your longest planned downtime for a service.

Running this over SSH on a headless server is fine. When you're managing a fleet, checking docker system df on each box one at a time is the part that gets skipped, and skipped cleanup is how disks fill to 100% and the daemon starts failing writes. If you'd rather see per-host disk usage and prune from your phone instead of SSHing into six servers, that's the kind of thing Docker HQ is built for, alongside a push alert before a host runs out of space rather than after. Watching disk trends is part of broader container monitoring, which is worth setting up regardless of how you prune.

Automating prune without regret

A cron entry that runs the safe steps weekly keeps most hosts healthy:

# /etc/cron.weekly/docker-cleanup
#!/bin/sh
docker image prune -f
docker builder prune --keep-storage 10gb -f
docker image prune -a --filter "until=336h" -f

Note the 336h (14 days) window for the aggressive step in automation. Give yourself more margin when no human is watching. Do not put --volumes in a cron script. Ever. If a job needs volume cleanup, that's a decision a person makes with docker volume ls open in front of them.

This matters more on small always-on machines. If you're running Docker on a home server, the same discipline applies, and our homelab getting-started guide covers the wider setup. The engine you use changes the commands slightly too; if you're on Podman, most of these map over but the flags differ in places, which we cover in the Docker vs Podman comparison.

Frequently asked questions

What's the difference between docker image prune and docker image prune -a?

docker image prune removes only dangling images, untagged layers left over from rebuilds. It's safe. docker image prune -a removes every image that isn't currently used by a container, including tagged images you may want to keep. Always add a --filter "until=.." to the -a version so you don't delete recent images.

Does pruning images delete my volumes or data?

No. docker image prune and docker builder prune never touch volumes. The command that deletes volumes is docker system prune --volumes, and only that flag. If you avoid --volumes, your persistent data is safe from any prune command.

How do I preview what a prune will delete?

docker image prune doesn't have a real dry-run, so list candidates first: docker images --filter "dangling=true" for the safe set, or docker system df -v to see every image with its size and container references. For build cache, docker builder prune prints the reclaimable amount and asks before deleting.

How often should I prune a production Docker host?

Weekly dangling-image and build-cache cleanup can be automated safely. Reserve the aggressive -a prune for a longer interval (every two weeks or monthly) with a generous until filter, and keep it out of any script that also handles volumes. Monitor disk usage so you prune on a schedule, not in a panic.

Start with docker system df on your fullest host right now. If the build cache line is the biggest number, you can reclaim most of it with one docker builder prune and never touch an image at all.