IAnswerable

Prune Docker Safely Without Deleting Production Data

17 Nov 2025

docker system prune -a --volumes is one command away from deleting a database volume you needed. It’s also, run carelessly, the most common way people learn what that volume contained the hard way. Pruning is safe — if you know which command touches which thing.

What each prune command actually deletes

Command Deletes Risk
docker container prune Stopped containers Low — they’re already stopped
docker image prune Dangling (untagged) images only Low
docker image prune -a All images not used by a running container Medium — includes tagged images you might redeploy from
docker network prune Unused custom networks Low
docker volume prune Volumes not referenced by any container High — this is where data loss happens
docker builder prune Build cache Low, just slows the next build
docker system prune Containers, images, networks (not volumes) Medium
docker system prune -a --volumes All of the above, including volumes Highest

The volume flag is the dangerous one, and it’s opt-in for a reason — docker system prune alone does not touch volumes. If you or a script adds --volumes, you’re saying “also delete anything not currently attached to a running container,” and that “not currently attached” condition is exactly the state a database volume is in for a few seconds during a routine docker compose restart.

1. Check what’s actually on the chopping block first

Never run a prune command blind on a server with real data. Dry-run equivalent — list what would be affected:

docker volume ls -f dangling=true
docker image ls -f dangling=true

For volumes specifically, cross-reference names against what your Compose files expect. A volume named myapp_pgdata disappearing from that list because it briefly wasn’t attached is a completely different situation from a volume named myapp_build_cache_tmp disappearing.

2. Prune specific categories, not everything at once

docker container prune -f
docker image prune -f
docker builder prune -f

This reclaims the vast majority of wasted space — old images, stopped containers, stale build cache — without ever touching volumes. Run this combination regularly (a weekly cron job is reasonable) with no real risk.

3. Handle volumes separately, deliberately

docker volume ls

Go through the list and identify which volumes are genuinely orphaned (from a service you removed entirely, or renamed) versus ones that are just temporarily detached because their container is mid-restart. Remove individually by name once you’re sure:

docker volume rm old-service-data

Never docker volume prune on a host running any stateful service — Postgres, Redis with persistence, uploaded file storage — without first confirming every volume you care about shows as in use by a currently running container. A container that’s stopped for a deploy, even briefly, makes its volume look “unused” to Docker in that instant.

4. Filter pruning by age instead of an all-or-nothing sweep

For images and containers, you can scope the prune to things older than a threshold, which is a safer default for automation than an unscoped sweep:

docker image prune -a --filter "until=168h"

This only removes images untouched for a week, giving you a buffer where anything from a recent deploy is protected even if it’s momentarily unused.

5. Automate the safe part only

If you want a cron job to keep disk usage down, scope it to what’s actually safe:

0 3 * * 0 docker container prune -f && docker image prune -af --filter "until=168h" && docker builder prune -f

Note what’s deliberately absent: no volume prune, no system prune -a --volumes. Volume cleanup stays a manual, deliberate action — it’s the one category where automation risk outweighs the disk space it saves.

The takeaway

docker system prune -a --volumes is fine to know about, not fine to run reflexively. Split your pruning into the safe automated part (containers, images by age, build cache) and the manual, deliberate part (volumes, checked by name against what you actually expect to see). If a script or a tutorial hands you the combined -a --volumes flag on a server with a database, read it twice before you run it.