Docker volume cleanup: finding and removing orphaned volumes

Run docker system df and the Local Volumes line keeps growing. Run docker system prune and you reclaim images and build cache, but the volume count barely drops. A few weeks later the disk alert fires again.

Data persists in /var/lib/docker/volumes/ after its consumer is long gone. These volumes waste disk, complicate capacity planning, and can hide sensitive data in forgotten corners of the filesystem.

This guide covers how Docker volumes become orphaned, how to distinguish unused volumes from named data stores that should stay, and how to remove them safely.

What this means

An orphaned volume is a Docker-managed volume that exists but is not referenced by any container. Unlike writable layers, volumes persist beyond a container’s lifecycle by default. When you run docker rm, its anonymous and named volumes survive unless you explicitly delete them.

Since Docker 22.06, docker volume prune removes only anonymous unused volumes by default. Named volumes require the --all flag.

A volume attached to a stopped container is not orphaned. Docker counts exited containers as references, so a volume may appear unused while still being held by a container in exited state. Remove the container before you can delete the volume.

Common causes

CauseWhat it looks likeFirst thing to check
Containers removed without -vdocker volume ls is large but docker ps -a shows few containersWhether existing volumes map to stopped containers
Expecting docker volume prune to clean named volumesPrune completes but named volumes remainDocker version and whether --all was used
Docker Compose anonymous volume misclassificationOld hash-like volume names accumulate after service recreatesdocker volume ls for Compose project labels
Daemon stale references after restartVolumes appear orphaned immediately after dockerd restartDaemon logs for volume driver errors
CI/CD or batch jobs creating short-lived containersAnonymous volumes grow on build or test runnersContainer creation and deletion rate on the host

Quick checks

# List volumes not referenced by any container
docker volume ls --filter dangling=true

Any volume listed here has no current consumer.

# Show Docker disk usage breakdown by type
docker system df

Check the Local Volumes line. If it is the largest component or growing faster than images, volumes are your target.

# Cross-reference volumes against all containers including stopped
docker volume ls -q | while read vol; do
  count=$(docker ps -a --filter volume="$vol" -q | wc -l)
  [ "$count" -eq 0 ] && echo "$vol (orphaned)"
done

This confirms that a volume has zero references. It is slow on hosts with hundreds of volumes.

# Check volume sizes directly on the host filesystem
sudo du -sh /var/lib/docker/volumes/*

Use this when docker system df is too slow or you need exact directory sizes.

# Inspect a specific volume for references and driver info
docker volume inspect <volume_name>

Check the Labels and Mountpoint fields to determine if the volume is anonymous, named, or managed by a custom driver.

# Check which containers mount a specific volume
docker ps -a --filter volume=<volume_name> --format '{{.ID}} {{.Names}} {{.Status}}'

If this returns results, the volume is not orphaned. Remove these containers first.

# Verify Docker version to know default prune behavior
docker version --format '{{.Server.Version}}'

Versions 22.06 and later default docker volume prune to anonymous volumes only.

# Review daemon logs for volume-related errors
journalctl -u docker.service --since "1 hour ago" | grep -iE "volume|error"

On systemd hosts, stale references or plugin failures often leave warnings here.

How to diagnose it

  1. Confirm volumes are the disk consumer. Run docker system df. If the Local Volumes section dominates usage or reclaimable space, focus on volume cleanup.

  2. List candidate orphans. Run docker volume ls --filter dangling=true.

  3. Verify no stopped containers hold references. For each candidate, run docker ps -a --filter volume=<name>. If a stopped container appears, the volume is not dangling. Remove the container before the volume.

  4. Distinguish anonymous from named. Anonymous volumes typically have long random names. Named volumes have human-readable names. Anonymous orphans are safe to prune with docker volume prune. Named orphans require docker volume prune --all.

  5. Inspect contents before deleting unknown volumes. Mount the volume temporarily to verify it is not a forgotten database or config store:

    # Inspect volume contents before removal
    docker run --rm -v <volume_name>:/data alpine ls -la /data
    
  6. Check for stale daemon references. If references seem inconsistent after an unclean restart, a clean daemon restart may reconcile state.

    WARNING: Restarting dockerd stops all containers unless live-restore is enabled. Plan for disruption.

  7. Correlate with container churn. If volume count tracks with CI/CD or batch job frequency, the root cause is missing -v flags in teardown scripts.

Metrics and signals to monitor

SignalWhy it mattersWarning sign
Orphaned volumes countUnused volumes waste disk and may contain sensitive datadangling=true count greater than 0 and increasing
Filesystem usage for /var/lib/dockerPersistent data growth leads to disk exhaustionVolume usage greater than 75% of available disk or growing faster than 5GB/day
Container creation/deletion rateHigh churn produces orphaned anonymous volumes if -v is not usedCreate/destroy rate greater than 10x baseline without cleanup
Docker daemon responsivenessStale volume references after restart require a healthy API to reconcile/_ping latency greater than 500ms or volume errors in daemon logs
Volume driver errorsPlugin or driver failures can leave volumes in limboError-level entries in daemon logs referencing volumes

Fixes

If containers were removed without volume cleanup

Remove containers with the -v flag to also delete anonymous volumes:

# Remove a container and its anonymous volumes
docker container rm -v <container_id>

For Compose environments, use docker compose down -v to remove both named and anonymous volumes declared in the file. docker compose rm -v does not remove named volumes declared in the compose file.

If named volumes survive prune

Modern Docker defaults to protecting named volumes. Run:

# Remove all unused volumes, including named ones
docker volume prune --all

This requires API version 1.42 (Docker 22.06+). Older daemons do not support this flag. Verify the daemon version before using it in automation.

If a stopped container blocks removal

docker volume rm --force does not override the in-use check. It only skips the confirmation prompt. Identify the consumer:

# Find containers referencing the volume
docker ps -a --filter volume=<volume_name> --format '{{.ID}}'

Remove the container, then the volume:

# Remove stopped container and orphaned volume
docker rm <container_id>
docker volume rm <volume_name>

If stale daemon references hide true orphans

After an unclean restart, volumes may appear unused while the daemon still tracks them.

# Restart Docker daemon (containers survive if live-restore is enabled)
systemctl restart docker

WARNING: This stops all containers unless live-restore is enabled. Plan for disruption.

Then re-run docker volume ls --filter dangling=true and remove any volumes that remain unreferenced.

If bind mounts are mistaken for volumes

docker volume prune never removes bind mounts. Verify the mount type:

# Check mount type for a container
docker inspect <container_id> --format '{{json .Mounts}}'

If "Type": "bind", reduce disk usage by cleaning the host path directly, not through Docker volume commands.

Prevention

  • Remove transient containers with -v so anonymous volumes do not accumulate.
  • Use docker compose down -v in CI/CD teardown scripts instead of docker compose rm or plain docker rm.
  • Monitor docker volume ls --filter dangling=true and alert on nonzero counts in production.
  • Set filesystem usage alerts for /var/lib/docker at 70% to catch growth before it blocks operations.
  • Document named volumes that are intentionally persistent so on-call engineers do not prune them.
  • Avoid scripting docker volume prune --all blindly in production; always verify that critical named volumes are mounted by running containers.

How Netdata helps

  • Disk usage monitoring on the filesystem hosting /var/lib/docker spots orphaned volume growth before disk-full alerts fire.
  • Container churn metrics identify workloads or CI pipelines that leak volumes through high creation and deletion rates.
  • Per-container volume mount visibility traces a suspicious volume back to its last consumer without manual CLI cross-referencing.
  • Filesystem utilization alerts for /var/lib/docker give early warning when orphaned volumes push disk usage toward critical thresholds.