docker『Device or Resource Busy』の原因と対処法

docker『Device or Resource Busy』の原因と対処法

「Device or resource busy」は、コンテナやボリュームを削除したり、マウントを外したりするときに、そのディスクやディレクトリがまだどこかで使用中だと出るエラー。bind mount 先をホスト側のプロセスが掴んだままになっていたり、コンテナ内プロセスがファイルを開いたまま終了していなかったりするのが典型パターン。どのマウント/ファイルを、どのプロセスが掴んでいるかを特定し、順番どおりに停止・アンマウントすることが解決のポイントになる。

エラーの意味と代表的な発生条件

「Device or resource busy」は、ほぼ OS カーネルから返されるエラーで、「まだ誰かが使っているので、このデバイス(ディスク/ディレクトリ/マウントポイント)は操作できない」という意味になる。docker 周りでよく出るタイミングは次のようなもの。

・bind mount しているディレクトリをホスト側で削除しようとしたとき

・コンテナがまだ利用しているボリュームを、docker volume rm や system prune で削除しようとしたとき

・umount でマウント解除しようとして「umount: target is busy」となるとき

・WSL2 や Docker Desktop で、Windows 側のエディタやアンチウイルスがファイルをロックしているとき

まずは「どのパスに対する操作で」「busy だと言われているか」をエラーメッセージから読み取る。

bind mount 先ディレクトリを消そうとして失敗するケース

ホスト側ディレクトリを bind mount でコンテナに渡しているとき、そのディレクトリを rm -rf しようとすると busy で止まることがある。

# コンテナ起動
docker run -d --name app \
  -v /data/app:/app/data \
  my-image

# ホスト側で削除しようとする
rm -rf /data/app
# → rm: cannot remove '/data/app': Device or resource busy

この場合、/data/app はコンテナ app にマウントされているので、まずコンテナを停止・削除する。

docker stop app
docker rm app

# その後に削除
rm -rf /data/app

bind mount しているパスは「コンテナが起動しているうちは busy になりやすい」ので、削除や再作成はコンテナ停止後に行う運用にしておく。

コンテナ内プロセスがファイルを掴んでいるケース

コンテナは止めたつもりでも、実際にはまだ実行中だったり、コンテナ内プロセスが特定のファイルを掴んだままになっていると、ボリュームの削除やマウント解除で busy エラーになる。

# コンテナが本当に止まっているか確認
docker ps -a

# 停止していなければ stop / kill
docker stop app
# どうしても止まらない場合
docker kill app

コンテナ側がまだ終了していないときに、ボリュームやディレクトリを操作すると busy になりやすい。

また、ホスト側で「どのプロセスがそのディレクトリを使っているか」を確認するには lsof や fuser が便利。

# ディレクトリを使っているプロセス一覧を出す
sudo lsof +D /data/app

# マウントポイントの利用状況を調べる
sudo lsof /data/app
sudo fuser -vm /data/app

ここにコンテナ由来のプロセス(containerd-shim, runc など)が残っている場合は、それらを伴うコンテナを stop / rm してから再度削除する。

docker volume rm / system prune での「resource busy」

docker volume rm で named volume を削除しようとして、以下のようなエラーになることがある。

docker volume rm appdata
# Error response from daemon:
#  remove appdata: volume is in use - [<container_id>]

これは、volume をマウントしているコンテナがまだ残っているため。対処の流れはシンプル。

# そのボリュームを使っているコンテナを確認
docker ps -a --filter volume=appdata

# 該当コンテナを停止して削除
docker stop <container_id>
docker rm <container_id>

# 再度ボリューム削除
docker volume rm appdata

docker system prune で一括削除中に同様のメッセージが出ることもあるが、その場合も「使用中のボリューム」が残っているだけなので、個別に volume rm + コンテナ整理を行う。

ホスト側 umount で「target is busy」になる場合

NFS や追加ディスクをマウントしていて、その上に Docker のデータや bind mount 先ディレクトリを置いていると、umount が busy で失敗することがある。

sudo umount /mnt/data
# umount: /mnt/data: target is busy

この場合もアプローチは同じで、「/mnt/data 配下を使っているプロセス」を特定して止める。

sudo lsof +D /mnt/data
sudo fuser -vm /mnt/data

どうしても解除したいがプロセスを止められない/ゾンビが残っている、といった状況では、lazy unmount を検討する。

sudo umount -l /mnt/data

ただし -l は「今後の参照を切る」だけで、既に開かれているハンドルは生き続ける点に注意が必要。本番環境では、原則としてプロセスを正常に終了させたうえで通常の umount を使う方が安全。

WSL2 / Docker Desktop でのファイルロック・アンチウイルスが原因のケース

Windows + Docker Desktop / WSL2 環境では、Windows 側のエディタやエクスプローラ、ウイルス対策ソフトがファイルをロックして「resource busy」につながることがある。例えば、VSCode で開いているディレクトリをコンテナの bind mount にしている場合など。

対処案:

・対象ディレクトリを開いているエディタ/エクスプローラを閉じる。

・一時的にアンチウイルスのリアルタイムスキャン対象から除外する。

・WSL2 セッションや Docker Desktop 自体を再起動して、古いファイルハンドルを解放する。

# WSL2 のリセット
wsl --shutdown

# Docker Desktop から "Restart Docker Desktop"

その後、docker ps / docker volume ls / rm を再試行し、エラーが解消されているかを確認する。

lsof / fuser を使った「誰が掴んでいるか」の特定手順

「Device or resource busy」は「誰かが掴んでいる」という意味なので、その「誰か」を特定するのが一番の近道になる。Linux なら lsof, fuser が定番。

# ディレクトリ全体を検索
sudo lsof +D /data/app

# マウントポイントに対するオープンファイルを一覧
sudo lsof /mnt/data

# fuserでプロセスID一覧を取得
sudo fuser -vm /data/app

表示された PID を見て、

・明らかに不要なプロセスなら kill する。

・コンテナ関連なら docker stop / rm で停止する。

# プロセスを停止(注意して使う)
sudo kill <pid>
# どうしても止まらない場合
sudo kill -9 <pid>

OSごと・環境ごとにツールは多少異なるが、「busy 対象のパスに対して、オープンしているプロセスを列挙する」考え方は共通。

コンテナ・ボリューム・マウントの削除順序を意識した運用

「Device or resource busy」を避けるためには、日常運用で「削除する順番」を決めておくと楽になる。例えば、bind mount を使っている場合の典型的な順番は次の通り。

  1. 該当コンテナを stop / rm
  2. 不要な volume を docker volume rm / prune で削除
  3. 最後にホスト側ディレクトリを rm -rf

    実際の一括削除例:
# プロジェクト配下のコンテナを停止・削除
docker compose down

# 名前付きボリュームも一緒に消す
docker compose down -v

# 最後にホスト側ディレクトリを削除
rm -rf ./data

この順番を守るだけでも、「busy で消せない」場面はかなり減る。

NG→OK 早見(よくあるパターンと修正例)

# 1) bind mount 先の削除
# NG
docker run -d --name app -v /data/app:/app/data my-image
rm -rf /data/app     # → Device or resource busy

# OK
docker stop app
docker rm app
rm -rf /data/app


# 2) volume 削除
# NG
docker volume rm appdata   # → volume is in use

# OK
docker ps -a --filter volume=appdata
docker stop <container>
docker rm <container>
docker volume rm appdata


# 3) umountがbusy
# NG
sudo umount /mnt/data      # → target is busy

# OK
sudo lsof +D /mnt/data
sudo fuser -vm /mnt/data
# 使用中プロセスを停止してから umount
sudo umount /mnt/data


# 4) WSL2 / Docker Desktopでのbusy
# NG: Windows側でフォルダを開きっぱなしのまま削除
rm -rf /mnt/c/work/data    # → Device or resource busy

# OK
# エディタ/エクスプローラを閉じる → WSL2とDockerを再起動
wsl --shutdown
# Docker Desktop: Restart

チェックリスト(上から順に確認する)

1) エラーメッセージに出ている「どのパス」「どのデバイス」が busy なのかを特定したか
2) そのパスを bind mount / volume で使っているコンテナが存在しないか(docker ps -a, docker volume lsで確認したか)
3) コンテナが本当に停止・削除されているか(State=exited かどうかをinspectで確認したか)
4) ホスト側で lsof / fuser を使って、そのパスをオープンしているプロセスを特定したか
5) WSL2 / Docker Desktop 環境では、Windows側アプリ(エディタ・エクスプローラ・アンチウイルス)がロックしていないか確認したか
6) NFS や追加ディスクのマウントポイントなら、umount前にそこを利用しているプロセスをすべて停止したか
7) lazy unmount(-l)や強制kill(-9)を使う前に、本当に安全かどうか(本番環境への影響がないか)を確認したか
8) 日常運用として「コンテナ → ボリューム → ホストディレクトリ → マウント」の順番で削除するルールを決めているか