Docker Error Guide: 'overlay2 failed to remove root filesystem: device or resource busy' — Clear Leaked Mounts
Fix Docker's overlay2 'device or resource busy' removal error: find the process or leaked mount holding the merged dir, unmount it, force-remove the container, and stop it recurring.
- #docker
- #troubleshooting
- #errors
- #overlay2
Stuck on this Docker with AI error? Get the free incident triage checklist
A one-page PDF — the exact steps to isolate, fix, and verify a production error like this one. No spam, unsubscribe anytime.
Overview
This error appears when Docker tries to delete a container’s storage but the kernel refuses because something still has the container’s merged directory mounted or open:
Error response from daemon: driver "overlay2" failed to remove root filesystem for 5f2c9a1b...: unlinkat /var/lib/docker/overlay2/9d4e7f.../merged: device or resource busy
device or resource busy is the kernel’s EBUSY. Docker cannot unlinkat (remove) the merged directory because it is still an active mount point or a process has a file open inside it. The container itself is usually already stopped — what remains is a leaked or lazy overlay mount that was never cleaned up, holding the directory hostage so docker rm cannot finish.
Symptoms
docker rm <container>ordocker rm -f <container>fails with the message above.- Containers pile up in
Removal In ProgressorDeadstate indocker ps -a. /var/lib/docker/overlay2/<hash>/mergedstill appears in/proc/mountseven though no container is running against it.- Disk usage in
/var/lib/docker/overlay2keeps growing because storage for “removed” containers is never actually reclaimed. - Frequently follows an ungraceful daemon restart, an OOM-killed container, or a bind-mount of a host path that was itself a mount point.
Common Root Causes
- A leaked overlay mount — the container stopped but its
mergedmount was never torn down, so the directory stays busy. - A process on the host holding a file open inside the
mergedtree (a backup agent, antivirus,du, or a shellcd-ed into the path). - Nested/lazy mounts — the container bind-mounted a host directory that was itself a mount point (e.g. an NFS share or another container’s volume), and the inner mount leaked into the host namespace.
- Systemd mount propagation — a
MountFlags=slave/shared-propagation interaction that copies container mounts into other namespaces where they linger. - A daemon that crashed mid-removal, leaving the storage layer half torn down.
Diagnostic Workflow
First find every mount that references the busy merged directory. Grep /proc/mounts for the container’s overlay hash from the error:
grep '9d4e7f' /proc/mounts
grep overlay2 /proc/mounts | grep merged
Find which process, if any, is holding a file open under that path:
sudo lsof +D /var/lib/docker/overlay2/9d4e7f.../merged 2>/dev/null
sudo fuser -vm /var/lib/docker/overlay2/9d4e7f.../merged
Confirm the container’s actual overlay directory and state via inspect:
docker inspect <container> --format '{{.GraphDriver.Data.MergedDir}}'
docker inspect <container> --format '{{.State.Status}}'
Read the daemon log for the removal failure and any earlier mount leak:
journalctl -u docker --since '30 min ago' | grep -i 'overlay2\|busy\|remove root'
If a stray mount is present, unmount it (lazily if a plain umount is also busy), then retry the removal:
sudo umount /var/lib/docker/overlay2/9d4e7f.../merged
# if still busy, detach lazily so the kernel cleans up when refs drop:
sudo umount -l /var/lib/docker/overlay2/9d4e7f.../merged
docker rm -f <container>
When mounts are truly stuck, restarting the daemon reconciles overlay2 state and releases leaked mounts:
sudo systemctl restart docker
docker rm -f <container>
Example Root Cause Analysis
A monitoring host accumulated dozens of Dead containers, and /var/lib/docker grew by several GB per day despite a nightly docker system prune. Every prune logged overlay2 failed to remove root filesystem ... device or resource busy.
grep merged /proc/mounts showed the leaked overlay mounts were still present. fuser -vm on one merged path returned nothing — no process held it open — which pointed at a leaked mount rather than an open file. The containers all shared one trait in docker inspect: they bind-mounted /mnt/nfs-logs, which was itself an NFS mount point. With the default mount propagation, each container’s view of that NFS mount leaked back into the host mount namespace, and when the container died the inner mount kept merged busy.
The immediate cleanup was a lazy unmount of each stuck merged directory followed by docker rm -f, then sudo systemctl restart docker to reconcile overlay2. The durable fix was to change the bind mount to use a private/rslave propagation so the NFS mount no longer leaked:
services:
collector:
image: myorg/collector:2.1
volumes:
- type: bind
source: /mnt/nfs-logs
target: /logs
bind:
propagation: rslave
After the propagation change the leaked mounts stopped appearing and disk reclamation returned to normal. The root cause was mount propagation from an NFS-backed bind mount, not a process holding files open — which the empty fuser output identified early.
Prevention Best Practices
- Avoid bind-mounting host paths that are themselves mount points (NFS, other volumes) without setting an explicit
propagationmode (rslave/private). - Never leave a shell or tooling
cd-ed into/var/lib/docker/overlay2/*/merged; keep backup/AV scanners from traversing the Docker data root. - Stop containers gracefully and avoid
kill -9on the daemon; ungraceful shutdowns are a common source of leaked mounts. - Alert on growth of
/var/lib/docker/overlay2and onDeadcontainers so leaks are caught before disk fills. - Keep the daemon and kernel current — several overlay2 mount-leak fixes have shipped over time.
Quick Command Reference
# Find mounts holding the busy merged dir (use the hash from the error)
grep '<hash>' /proc/mounts
grep overlay2 /proc/mounts | grep merged
# Who is holding it?
sudo lsof +D /var/lib/docker/overlay2/<hash>/merged
sudo fuser -vm /var/lib/docker/overlay2/<hash>/merged
# The container's actual merged dir
docker inspect <container> --format '{{.GraphDriver.Data.MergedDir}}'
# Unmount (lazy if needed) then force-remove
sudo umount /var/lib/docker/overlay2/<hash>/merged
sudo umount -l /var/lib/docker/overlay2/<hash>/merged
docker rm -f <container>
# Reconcile overlay2 state
sudo systemctl restart docker
Conclusion
overlay2 failed to remove root filesystem ... device or resource busy is an EBUSY on the container’s merged directory: the container is gone but a mount or open file still holds its storage. Diagnose in order — grep /proc/mounts for the overlay hash, use fuser/lsof to separate a leaked mount from an open file, unmount (lazily if necessary), then docker rm -f. A daemon restart clears stubborn cases. The lasting fix is upstream of removal: control mount propagation on bind mounts, keep processes out of the Docker data root, and shut containers and the daemon down gracefully so mounts never leak in the first place.
Fixed it? Get 500 Docker with AI & DevOps AI prompts — free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.
Stuck on this? Start guided troubleshooting
Open an interactive diagnostic session with this error already loaded. Work a step-by-step plan, record what each check returns, land on a root cause, and export a clean incident summary — no account needed to start.
Did this fix your issue?
Solved it a different way?
Share the fix that worked for you — reviewed, then published to help the next engineer.
That looks like it may contain a secret (key, token, password, or connection string). Please remove it — a note with a detected secret can’t be published.
Thanks — that helps. Published notes appear after a quick review.
Trending errors this week
The error guides other engineers are actually reading right now.
- 1mount: wrong fs type, bad option, bad superblock
- 2Docker 'failed to set up container networking': Fix the Bridge and IP Pool
- 3Docker 'failed to create shim task': How to Fix the containerd Runtime Error
- 4Transport endpoint is not connected
- 5modprobe: FATAL: Module not found
- 6mount: wrong fs type, bad option, bad superblock
Get 500 Battle-Tested DevOps AI Prompts — Free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.