How to Fix 'Docker Volume Is In Use' Error After docker rm -f (Volume Removal Conflict Guide)
Threat/Impact Level: HIGH | Downtime Risk: HIGH | Time to Fix: 5–15 mins
TL;DR
- What broke: Docker's volume reference counter is non-zero despite
docker rm -f. A stopped container, an anonymous volume mount, a Compose network, or a lingering Swarm task still holds the ref. - How to fix it: Identify all consumers with
docker ps -a --filter volume=<name>anddocker inspect, prune dangling refs, thendocker volume rm. - Fast path: Use our Client-Side Sandbox above to paste your
docker inspectordocker-compose.ymloutput and auto-generate the exact removal sequence — your config never leaves the browser.
The Incident (What Does the Error Mean?)
You ran:
docker rm -f my_container
docker volume rm my_data_volume
Docker returns:
Error response from daemon: remove my_data_volume: volume is in use -
[a3f1c2d4e5b6f7a8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2]
That hex string is a container ID that Docker's volume reference table still tracks. docker rm -f sends SIGKILL and removes the container record from docker ps, but the daemon's internal volumestore reference counter is decremented asynchronously. In practice, four scenarios keep that counter above zero:
- A second, non-obvious container (init container, sidecar, exec session) still mounts the volume.
- Anonymous volumes created by a
VOLUMEdirective in the Dockerfile — Compose tracks these separately from named volumes. - A Compose project network or service that was not fully downed (
docker-compose downvsdocker-compose stop). - A Swarm task replica in
shutdownstate that hasn't been garbage-collected yet.
The Attack Vector / Blast Radius
This is a disk leak and deployment blocker, not a security exploit — but the blast radius in CI/CD pipelines is severe:
- Build agents accumulate gigabytes of orphaned volumes. On a busy Jenkins/GitLab runner, this silently fills
/var/lib/dockeruntil the host OOM-kills the Docker daemon itself, taking every running container with it. - Kubernetes-adjacent stacks using Docker-in-Docker (DinD) hit this constantly. A stuck volume inside a DinD pod causes the pod to fail
PreStophooks, resulting in forced pod termination and potential data corruption on whatever was writing to that volume. - Secret material persists on disk. If the volume held
.envfiles, TLS private keys, or database seeds, the data remains readable at/var/lib/docker/volumes/<name>/_databy any process with root on the host — indefinitely — until the volume is actually removed.
How to Fix It
Step 1 — Identify every consumer
# Find ALL containers (running + stopped) referencing the volume
docker ps -a --filter volume=my_data_volume --format 'table {{.ID}}\t{{.Names}}\t{{.Status}}'
# Cross-check the volume's Mounts section
docker volume inspect my_data_volume
The inspect output will show "RefCount" (internal, not always exposed) and the "Mountpoint". The error message's hex ID is your primary target.
Step 2 — Force-remove ALL referencing containers
# Remove every container the filter found
docker ps -a --filter volume=my_data_volume -q | xargs --no-run-if-empty docker rm -f
Step 3 — Retry volume removal
docker volume rm my_data_volume
If it still fails, the reference is from a Compose project:
# Nuclear Compose teardown — removes containers, networks, AND named volumes
docker-compose down --volumes --remove-orphans
Basic Fix (diff)
- docker stop my_container
- docker rm my_container
- docker volume rm my_data_volume
# ^^^ Fails. rm without -f leaves stop/start race; single rm misses sidecars.
+ docker ps -a --filter volume=my_data_volume -q | xargs --no-run-if-empty docker rm -f
+ docker volume rm my_data_volume
Enterprise Best Practice (diff)
For Compose-managed stacks, the root cause is almost always docker-compose stop instead of docker-compose down. Stop halts containers but does not release volume references.
# docker-compose.yml — enforce explicit volume lifecycle
volumes:
- my_data_volume:
+ my_data_volume:
+ labels:
+ com.myorg.managed: "true"
+ com.myorg.cleanup_policy: "on_down"
# Teardown script
- docker-compose stop
+ docker-compose down --volumes --remove-orphans
+ # In CI: add `docker volume prune -f --filter label=com.myorg.cleanup_policy=on_down`
For Swarm:
- docker service rm my_service
- docker volume rm my_data_volume
# Swarm tasks linger in shutdown state for up to 5s
+ docker service rm my_service
+ sleep 10 # or poll: until [ -z "$(docker ps -a --filter volume=my_data_volume -q)" ]; do sleep 1; done
+ docker volume rm my_data_volume
💡 Tired of pasting proprietary configs into ChatGPT? Generic AI tools log your company's ARNs, DB strings, and private keys. StackEngine is a zero-backend, pure Client-Side WASM utility. Drop your failing
docker inspectordocker-compose.ymlinto the sandbox above. We redact your secrets locally in the browser and auto-generate the refactored code using your own API key.
Prevention in CI/CD
1. Enforce down --volumes in pipeline teardown
Every CI job that spins up Compose services must use a trap:
trap 'docker-compose down --volumes --remove-orphans' EXIT ERR
docker-compose up -d
run_tests
2. Add a volume leak check as a pipeline gate
# Post-job assertion — fail the build if orphaned volumes exist
ORPHANS=$(docker volume ls -qf dangling=true)
if [ -n "$ORPHANS" ]; then
echo "LEAKED VOLUMES: $ORPHANS" >&2
docker volume rm $ORPHANS
exit 1 # or just warn, depending on policy
fi
3. OPA / Conftest policy for Compose files
# policy/docker_compose_volumes.rego
package compose
deny[msg] {
input.services[svc].volumes[_]
not input.volumes # named volumes block must exist
msg := sprintf("Service '%v' uses volumes but no top-level volumes block defined — anonymous volumes will leak", [svc])
}
Run in CI:
conftest test docker-compose.yml --policy policy/
4. Periodic prune in long-lived environments
# Cron or systemd timer — weekly volume prune
docker volume prune -f --filter label!=com.myorg.persistent=true
Label volumes that must survive reboots with com.myorg.persistent=true. Everything else gets reaped.