最初の1分で、容量とinodeのどちらが尽きたかを確かめる

「No space left on device」は、ディスクの容量が尽きたときにも、inodeが尽きたときにも出ます。inodeは、ファイル1つごとに使われる管理情報の枠です。数に上限があり、小さなファイルが大量にあると容量に余裕があっても枠のほうが先に尽きます。原因によって探す場所が変わるため、最初に次の2つのコマンドを実行します。

df -h
df -i

df -h の結果で、/ や /var の Use% が100%か、それに近ければ容量不足です。この場合は次の節に進みます。df -h には余裕があり、df -i の IUse% が100%なら inode不足です。この場合は「inodeが尽きている場合」の節に進みます。

ext4では、ブロックの約5%がroot用に予約されています。そのため一般ユーザーのプロセスが書き込めなくなっても、rootなら少しだけ書き込める状態が残っていることがあります。作業はrootかsudoで進めます。

容量が尽きている場合の絞り込み

du で大きいディレクトリを探す

まず、どのディレクトリが容量を使っているかを調べます。

sudo du -xh --max-depth=1 / 2>/dev/null | sort -h

-x は、別のファイルシステムをまたがないための指定です。結果で大きいディレクトリを見つけたら、パスを指定して同じコマンドを繰り返し、1階層ずつ下ります。多くの場合は /var/log、/var/lib/docker、/var/lib/mysql、バックアップの保存先のどれかが原因です。ncdu を使えば対話的に探せますが、ディスクが満杯だとインストールできない場合があります。

ログの肥大を確認する

/var/log が大きい場合は、アプリのログ、Webサーバーのアクセスログ、journald のどれかが膨らんでいます。journald の使用量は journalctl --disk-usage で確認できます。古いログを削るには sudo journalctl --vacuum-size=200M を実行します。

書き込み中のログファイルを rm で消してはいけません。プロセスがファイルを開いたままだと、削除しても容量が戻らないためです(後述)。中身だけを空にするなら sudo truncate -s 0 /var/log/xxx.log を使います。調査に必要なログであれば、先に末尾だけを別の場所へ退避します。

Dockerのイメージ・ボリューム・ログを確認する

/var/lib/docker が大きい場合は、docker system df で内訳を確認します。使っていないイメージは docker image prune -a で、ビルドキャッシュは docker builder prune で削除できます。

docker volume prune はデータを失うおそれがあるため注意が必要です。DBのデータをボリュームに置いている場合、そのコンテナが停止中だとボリュームが「未使用」扱いになることがあります。実行する前に docker volume ls で中身を確かめます。もう1つ見落としやすいのがコンテナのログです。標準の json-file ドライバは上限なしで書き続けるため、/var/lib/docker/containers/ の下にある *-json.log が数GBまで膨らむことがあります。

削除済みのファイルをプロセスが掴んだままになっていないか確認する

df では満杯なのに、du で合計しても容量が合わないことがあります。その場合は、削除済みのファイルをプロセスが開いたままになっている可能性が高いです。次のコマンドで確認します。

sudo lsof +L1

出力に (deleted) と表示された大きなファイルがあれば、それを開いているプロセスを再起動すると容量が戻ります。すぐに再起動できない場合は、PID と FD 番号を確認したうえで sudo sh -c ': > /proc/PID/fd/FD番号' を実行すれば中身を空にできます。

バックアップ・DBのログ・パッケージのキャッシュを確認する

ここまでで見つからない場合は、溜まり続けるものを疑います。たとえば、cronで毎日作る mysqldump のファイル、MySQLのバイナリログ、パッケージのキャッシュなどです。パッケージのキャッシュは、Debian/Ubuntu系なら sudo apt-get clean、RHEL系なら sudo dnf clean all で削除できます。

MySQLのバイナリログは、/var/lib/mysql の下にあるファイルを直接消さないでください。PURGE BINARY LOGS BEFORE '2026-10-01 00:00:00'; のようにSQLで削除します。レプリケーションを使っている場合は、レプリカが読み終えた位置より前だけを削除します。

inodeが尽きている場合

inode不足のときは、ファイルの数が多いディレクトリを探します。

sudo du --inodes -x -d 1 / 2>/dev/null | sort -n

原因になりやすいのは、PHPのセッションファイル、メールキュー、CMSやアプリが作るキャッシュの小さなファイルです。ファイル数が非常に多いと、rm * は引数が長すぎて失敗します。そのため find /path -type f -mtime +7 -delete のように、条件を付けて find で削除します。あわせて、ファイルが増え続ける仕組み自体も止めます。PHPのセッションであれば、ガベージコレクションが動いているかを確認します。

空き容量を作ったあとにサービスを戻す

容量を空けても、止まったサービスは自動では戻らないことがあります。systemctl status nginx mysql などで状態を確認し、必要なサービスを再起動します。DBが起動しない場合は、journalctl -u mysql などでエラーを読みます。書き込みの途中で止まった場合は、DBの起動時にリカバリが走ることがあります。ログに破損の警告が出ていたら、バックアップから戻すことも検討します。

削れるものがない場合はディスクを広げる

不要なデータがなく、正当なデータだけで満杯になっている場合は、削除では解決しません。プランの変更か追加ディスクで容量を増やします。ディスクを拡張しても、パーティションとファイルシステムは自動では広がらないことがあります。その場合は、growpart でパーティションを広げてから、ext4なら resize2fs、XFSなら xfs_growfs を実行します。手順は事業者ごとに違うため、作業前に公式のマニュアルを確認し、スナップショットを取っておきます。

再発を防ぐ:ログの上限設定と使用率の監視

アプリのログは、logrotateでローテーション(古いログの切り替えと削除)を設定します。たとえば /etc/logrotate.d/myapp に次のように書きます。

/var/log/myapp/*.log {
    daily
    rotate 14
    compress
    delaycompress
    missingok
    notifempty
    copytruncate
}

sudo logrotate -d /etc/logrotate.d/myapp を実行すると、ファイルを変更せずに動作を確認できます。journaldの上限は、/etc/systemd/journald.conf に SystemMaxUse=500M のように書いて設定します。設定後に journald を再起動すると反映されます。Dockerのログは、/etc/docker/daemon.json に {"log-driver": "json-file", "log-opts": {"max-size": "10m", "max-file": "3"}} と書いて上限を設けます。この設定は、作り直したコンテナにだけ適用されます。

監視では、容量の使用率とinodeの使用率の両方に閾値を設けます。手段としては、事業者が提供する監視機能、Zabbix、Prometheusとnode_exporterの組み合わせ、Netdataなどがあります。最小限の対策なら、cronで df の結果を確認して通知するスクリプトでも構いません。目安として、80%で警告を出し、90%で対応を始めます。

容量を空けて落ち着いたら、今回の原因が何だったかを確かめてください。ログやキャッシュのように溜まり続けるものが原因なら、上限を設定すれば解決します。データの増加そのものが原因なら、容量の拡張を計画に入れる必要があります。