SSD Nodes Learn Hosting plans →
ガイド Matt Connor著者 Matt Connor ・更新日 2026-08-26

dfはfullなのにduで空きがある原因と確認方法

dfはfullなのにduで容量を確認できない原因を解説します。削除済みファイルを開いたままのプロセスをlsofで特定し、再起動せずに解放する方法や、inode枯渇、隠れたマウント、予約ブロックも確認できます。

Verified Every command ran end-to-end on a fresh Ubuntu 24.04 server, August 15, 2026.

df が full と報告し、du では一致しない理由

df はディスクが full だと報告しますが、du では使用領域を特定できません。これは、削除済みファイルをプロセスが引き続き保持しているためです。ファイルを削除すると、ディレクトリからその名前が削除されます。データブロックが解放されるのは、その inode を指す最後のオープンファイルディスクリプターが閉じられたときだけです。du はファイル名をたどるため、削除済みファイルの領域を数えません。df はファイルシステムに割り当て済みブロック数を問い合わせるため、名前がなくなったファイルも引き続き数えます。

このガイドでは、すでにインストールされているツールを使い、通常の Ubuntu VPS 上でこの状態を再現します。/proc でファイルを保持しているプロセスを特定し、再起動せずに領域を解放します。同じ症状を引き起こす別の原因として、空き inode がない inode テーブル、マウントポイントの下に隠れているファイル、root 用に予約されたブロックについても説明します。

各コマンドを実行し、自分の環境で出力を確認してください。値はディスクによって異なります。そのため、ガイドに表示された値と比較するのではなく、自分のマシンで実行前後の結果を比較してください。

df が数えるものと du が数えるもの

df(disk free)は、マウントされている各ファイルシステムに対して、それぞれの使用状況を問い合わせます。存在するブロック数、割り当て済みブロック数、空きブロック数を確認します。ディレクトリを開くことはありません。結果には、割り当て済みのすべてのブロックが含まれます。どのディレクトリエントリからも参照されていないファイルのブロックも対象です。

du(disk usage)は逆の動作をします。指定されたパスから開始し、ディレクトリを読み取り、見つかったすべてのエントリに対して stat を実行し、ブロック数を合計します。名前のないファイルは認識できません。読み取りを許可されていないディレクトリも同様です。そのため、一般ユーザーの合計値は root より小さくなります。比較結果から判断する前に、dusudo で実行してください。

2 つを比較する場合は、毎回 2 つのオプションが重要です。

  • -x は、du の対象を 1 つのファイルシステムに限定します。これを指定しないと、du // の下にマウントされたすべてのファイルシステムへ移動し、df / が測定していない範囲まで含めた合計を出力します。
  • -s は、ディレクトリごとに 1 行出力する代わりに、引数ごとに 1 行の集計を出力します。

これで、確認したいファイルシステム上で並べて実行するコマンドの組み合わせが得られます。

df -h /
sudo du -xhs / 2>/dev/null

df はすぐに結果を返します。du は、すべてのファイルを順に stat するため、大規模なファイルシステムでは数分かかります。2 つの合計値が大きく異なり、du を root として -x 付きで実行した場合、失われている容量は名前のない何らかの対象に割り当てられています。

意図的に不一致を再現する

テスト用の VPS で実行してください。以下はすべて bash と coreutils で行うため、何もインストールされません。

/var/tmp を保持するファイルシステムの開始時点の状態を記録します。

cd /var/tmp
df -h .
df --output=used -B1 .

2 番目のコマンドは、丸めずに使用済みバイト数を表示します。そのため、最後の確認を正確に行えます。

次に、ファイルを作成します。ファイルサイズには、マシン自身が報告する空き容量を使用するため、どの容量のディスクでも実行できます。

free=$(df --output=avail -B1 . | tail -n 1)
fallocate -l $((free / 10)) ghost.bin
ls -l ghost.bin
df -h .

$(...) はコマンド置換です。シェルがその中のコマンドを実行し、出力を free の値として使用します。この構文に慣れていない場合は、bash のコマンド置換で詳しく説明しています。fallocate は実際のブロックを確保しますが、書き込みは行いません。そのため、処理はすぐに完了します。これに対応していないファイルシステムではコマンドが失敗します。その場合、head -c $((free / 10)) /dev/zero > ghost.bin がバイトを書き出して同じ処理を行います。

この df -h . と、記録した値を比較します。使用済みの列は増え、空き容量の列は減っています。

次に、別のプロセスでファイルを開いたままにしてから、ファイルを削除します。

sleep infinity < ghost.bin &
holder=$!
rm ghost.bin
ls -l ghost.bin
df -h .
sudo du -xhs . 2>/dev/null

仕組みの要点はリダイレクトです。sleep infinity < ghost.bin & は、そのファイルを標準入力にするバックグラウンドプロセスを起動します。シェルはファイルを開き、そのファイルディスクリプターを sleep に渡します。sleep はファイルを開いたままにします。$! には、そのバックグラウンドジョブのプロセス ID が格納されます。rm は、ファイルディスクリプターが開いたままの状態で名前を削除します。

出力を確認します。ls はファイルを見つけられません。ファイル名がなくなったためです。du は、名前をたどって処理するため、開始時点に近い値へ戻っています。df は変化していません。ブロックがまだ割り当てられているためです。これで、ファイルシステムとディレクトリツリーの状態が一致しなくなりました。この差が、削除したファイルです。

削除されたファイルを保持しているプロセスを特定する

開いているファイルディスクリプターはすべて、/proc/<pid>/fd/ 配下に、参照先のファイルへのシンボリックリンクとして表示されます。ファイルのリンクが解除されると、カーネルはそのリンクの対象を deleted としてマークします。したがって、保持元を特定するには、対象にそのマーカーが付いたリンクを探します。

sudo find /proc/[0-9]*/fd -lname '*(deleted)' -printf '%p -> %l\n' 2>/dev/null

-lname は名前ではなくシンボリックリンクの対象に一致し、%p はディスクリプターのパスを表示し、%l はリンク先を表示します。プロセス ID は、表示されたパスの 2 番目の要素です。自分のプロセス以外の /proc/<pid>/fd は通常読み取れないため、sudo を付けて実行します。find の走査中に終了したプロセスから出るエラーは、標準エラー出力のリダイレクトで破棄します。

負荷の高いサーバーでは、常に複数の削除済みファイルが保持されています。その大半は小さく、問題のないファイルです。サイズ順に並べ、確認すべきものだけを先頭に集めます。

sudo bash -c 'for fd in /proc/[0-9]*/fd/*; do
  target=$(readlink "$fd" 2>/dev/null) || continue
  case "$target" in
    *"(deleted)") echo "$(stat -Lc %s "$fd" 2>/dev/null) $fd $target" ;;
  esac
done' | sort -rn | head

stat -L はリンクをたどって inode 自体を参照するため、%s は名前を失ったファイルのサイズを報告します。その値でソートすると、最大のファイルが先頭になります。

次に、先頭にあるディスクリプターの背後で動作しているプロセスを特定します。リストの先頭のパスには必要な 2 つの番号が含まれているため、まず変数に代入します。PID と N は、自分の環境で表示された値に置き換えてください。

pid=PID
n=N
ps -o pid,user,etime,args -p "$pid"
sudo stat -L "/proc/$pid/fd/$n"

ps はプログラム名と稼働時間を表示します。stat -L は削除された inode のサイズと割り当て済みブロック数を表示します。これらを組み合わせると、重要な問い、つまりどのサービスがこのファイルを保持しているのかが分かります。

マシンにすでに lsof がある場合、sudo lsof +L1 はリンク数が 0 になった開いているファイルを一覧表示し、サイズを 1 つの表にまとめます。最小構成の Ubuntu イメージには含まれていません。また、空き容量のないファイルシステムでは、パッケージのインストール自体が失敗する可能性があります。そのため、/proc による走査が常に使える方法です。

再起動せずに空き容量を回復する

再起動で解決はしますが、最初に行うべき方法ではありません。サービスが停止し、証拠も失われるためです。より影響の小さい方法は4つあります。試す順番もこのとおりです。

データが必要なら、まず別の場所へコピーします。descriptor のパスを読み取ると、現在の inode を読み取れます。

sudo cp /proc/<pid>/fd/<n> /root/recovered.log

削除済みファイルを簡単に取り戻せるのは、このケースだけです。そのため、rm -rf で削除したファイルを復元する手順では、プロセスがまだファイルを開いたままかどうかを最初に確認します。最後の descriptor が閉じると、この方法は使えません。

次に、descriptor 経由でファイルを空にします。/proc のパスは同じ inode を指すため、truncate するとプロセスを実行したままブロックを解放できます。

sudo truncate -s 0 "/proc/$pid/fd/$n"
df -h /

書き込み元がファイルを append モードで開いている場合、この方法は問題なく動作します。その場合、すべての書き込みが現在のファイル末尾に行われるためです。append モードでなかった場合、プロセスは以前の書き込みオフセットを保持します。そのため、次の書き込みはファイル内の遠い位置に行われ、先頭に hole がある状態でファイルが再作成されます。hole には領域が割り当てられないため、ブロックは空きのままです。df には、解放した空き容量が引き続き表示されます。変化するのはファイルサイズだけです。プロセスが書き込んだ後に sudo stat -L "/proc/$pid/fd/$n" を再実行すると、以前のサイズと一致しないブロック数が表示されます。サイズも0から始めたい場合は、プロセスを再起動します。

3つ目は、サービスにログの再オープンを要求する方法です。ログファイルを削除された状態で使い続けている daemon は、この問題でよくある実例です。多くの daemon はシグナルを受け取るとログファイルを再オープンします。nginx は SIGUSR1、rsyslog は SIGHUP を使用します。推測せず、対象の daemon のドキュメントを確認してください。誤った daemon に誤ったシグナルを送ると、停止する可能性があります。

sudo systemctl kill -s USR1 nginx

これは、systemd が unit のメインプロセスとして記録しているプロセスにシグナルを送ります。そのため、daemon の実際の起動方法に対して 誤った Type= を指定している unit では、削除済みファイルを保持していなかったプロセスにシグナルが送られることがあります。その場合、空き容量は回復しません。

4つ目は、unit を再起動する方法です。sudo systemctl restart <unit> により、以前のプロセスが保持していたすべての descriptor が閉じられるため、ブロックは確実に戻ります。上の例では、保持しているのは自分で起動した sleep なので、これを終了するだけで十分です。

kill $holder
df -h .
df --output=used -B1 .
sudo find /proc/[0-9]*/fd -lname '*(deleted)' -printf '%p -> %l\n' 2>/dev/null

使用済みバイト数を、ファイル作成前に記録した値と比較します。再び一致し、find には descriptor が表示されなくなります。問題を見つけたコマンドと同じコマンドで確認する習慣を身につけてください。

この値の変化を確認するには、df を何度も手動で実行するより、監視するほうが簡単です。watch は一定間隔でコマンドを繰り返すとともに、出力を同じ場所に再表示します。そのため、watch df -h / では空き容量が戻るにつれて使用済み列が変化する様子を確認できます。

合計値が一致しているのにディスクがまだ満杯の場合

df と root du -x の結果が一致している場合、削除済みファイルは関係していません。残る原因は別の種類であり、それぞれ確認方法が異なります。

inode 枯渇はブロック枯渇とは異なります

inode には 1 つのファイルのメタデータが格納されます。ext4 はファイルシステムの作成時に固定数の inode を作成するため、空きブロックが残っていても inode が枯渇することがあります。その場合、df -h に空きが表示されていても、新しいファイルの作成に失敗します。

df -h /
df -i /

最初のコマンドはブロック数を数え、2 番目のコマンドは inode 数を数えます。それぞれの use 列を比較してください。ブロック使用率が低く、inode 使用率が上限に達している場合、非常に小さいファイルが大量に存在することが原因です。

df は同じ呼び出しで -i--output を拒否します。そのため、未加工のカウントを読み取る場合や別のコマンドに渡す場合は、inode フィールドを名前で選択し、-i は付けないでください。

df --output=itotal,iused,iavail,ipcent /

これらの列には df -i が出力するものと同じ集計値が、分解しやすい形式で表示されます。

バイト数ではなくエントリ数を数えてファイルを探します。

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

最上位に出たディレクトリを対象に、同じコマンドを 1 階層下で繰り返します。ファイルを作成しているツリーに到達するまで続けてください。使用している du--inodes に対応していない場合は、sudo find /var -xdev -type f | wc -l でサブツリーを時間をかけて数えます。

対処方法は、それらのファイルを削除または移動することです。既存の ext4 ファイルシステムに inode を追加することはできません。inode 数は mkfs 時点で固定されるため、増やすにはファイルシステムを再作成し、バックアップから復元する必要があります。XFS は必要に応じて inode を割り当てるため、同じような固定上限には達しません。コンテナを実行するマシンでは、イメージレイヤーに小さいファイルが多数含まれるため、通常より早く両方の上限に達します。そのマシンでは VPS 上の Docker のディスク使用量を削減する ことが具体的な対処方法です。ファイルシステム全体を一般的に整理するよりも、はるかに多くの容量を回収できます。

マウントポイントの下に隠れた領域

ディレクトリには、その上に何もマウントされていない段階でファイルを置けます。そのディレクトリにファイルシステムをマウントしても、下にあるファイルは元の場所に残ります。領域は確保されたまま、df にもカウントされますが、名前ではアクセスできなくなります。マウントによって覆われるため、du からは見えません。

余分なディスクを必要としない tmpfs で確認します。この手順にはマウント権限のあるマシンが必要です。そのため、KVM VPS で実行できます。

sudo mkdir -p /srv/covered
sudo cp /etc/services /srv/covered/
ls /srv/covered
sudo mount -t tmpfs tmpfs /srv/covered
ls /srv/covered
sudo umount /srv/covered
ls /srv/covered

中央の ls には空のディレクトリが表示されます。コピー先がなくなったわけではありません。ファイルは root filesystem 上に残っており、アンマウントすると再び現れます。あるサービスが、そのパスに 1 か月間ログを書き込んでから、誰かがその上に volume をマウントした場合を考えてください。

稼働中のサーバーで実体を確認するには、root filesystem を別の場所にもう一度マウントします。bind mount を使うと、その中にマウントされたファイルシステムを除外して、1 つのファイルシステムを表示できます。

sudo mkdir -p /mnt/rootcheck
sudo mount --bind / /mnt/rootcheck
sudo du -xhs /mnt/rootcheck/* 2>/dev/null | sort -h
sudo umount /mnt/rootcheck

通常のパスの下には表示されないのに、その一覧には表示されるものは、マウントポイントの下に隠れています。作業が終わったら bind mount をアンマウントしてください。そうしないと、後で -x なしで実行する du が同じファイルを 2 回カウントします。

root 用に予約されたブロック

ext4 は、root ユーザー用にブロックの一部を確保します。これにより、ディスクが満杯になっても root がログインしてシステムを修復できます。一般ユーザーとして実行されるプロセスは先にこの制限に達しますが、df にはまだ少し空きがあるように表示されます。デフォルト値を前提にせず、使用中のファイルシステムで設定を確認してください。

dev=$(df --output=source / | tail -n 1)
sudo tune2fs -l "$dev" | grep -i 'block count'

これにより、総ブロック数と予約済みブロック数が同じ単位で表示されるため、両者の比率を直接確認できます。df は、一般ユーザーがまだ使用できる空き容量として available 列を表示します。そのため、used と available の合計は size より小さくなります。この差が予約領域です。

sudo tune2fs -m <percent> "$dev" で変更できます。変更は直ちに適用され、再マウントは必要ありません。独立したデータファイルシステムでは、予約領域を減らしても問題ありません。root ファイルシステムでは、root が書き込めるだけの領域を残してください。root ファイルシステムに空きがまったくないと、修復が非常に困難になるためです。また、この領域はログイン不能を防ぐためにも必要です。空き容量のないファイルシステムで authorized_keys に鍵を追加すると、書き込みが途中で切れるか、まったく書き込めないことがあります。その場合、次回のログインでは、鍵自体に問題がないにもかかわらず Permission denied (publickey) が返されます。tune2fs は ext2、ext3、ext4 で使用できます。XFS には同等の設定がありません。

単独で du を使うと誤解しやすい場面

duには、合計値が誤って見える4つの習慣があります。

  • ハードリンク: duは、複数の名前が同じ inode を指していても、inode を1回だけ数えます。そのため、ハードリンクで構成されたツリーでは、ファイルの合計より小さい値が表示されます。
  • スパースファイル: duは実際に割り当てられたブロックを報告します。一方、ls -lは見かけ上のサイズを報告します。もう一方の値を見るには --apparent-size を追加します。
  • 権限: 一般ユーザーとして実行すると、duは読み取れないものをスキップするため、実際より少ない値を報告します。表示されるエラーを /dev/null にリダイレクトして、確認を止める人もいます。
  • ファイルシステムの境界: -xを指定しないと、du // 以下にマウントされたすべてのファイルシステムを数えます。そのため、合計が df / の報告値を超えることがあります。

dfにも、知っておくべき習慣が1つあります。ファイルシステムごとに個別に報告するため、書き込みが失敗した対象の正確なパスに対して実行してください。別の /boot は、kernel package の蓄積に応じて独自のスケジュールで容量を消費します。Ubuntu で古い kernel を削除する作業は、/の空き容量を確保する作業とは異なります。

実際のインシデントでの対応手順

  1. 失敗した書き込みの対象ファイルシステム上で df -h <path>df -i <path> を実行します。習慣的に / 上で実行してはいけません。
  2. sudo du -xh -d 1 <mountpoint> 2>/dev/null | sort -h を実行し、使用量が最大のディレクトリを順に調べます。
  3. df が使用済みとして報告した容量を du で説明できない場合は、/proc を検索して、削除済みでありながら開いたままのファイルを探します。
  4. 2 つの結果が一致する場合は、ファイルシステムを別の場所に bind mount し、マウントポイント配下に残っているファイルを探します。
  5. 上限に達しているのが inode 使用量の場合は、バイト数ではなくファイル数を数えます。

ここでの各手順には、出力を読み取れるコマンドがあります。これが、推測ではなく原因を解消するための違いです。

FAQ

df ではディスクが満杯と表示されるのに、du ではそれより大幅に少なくなるのはなぜですか?

通常の原因は、プロセスが開いたままのファイルを削除したことです。ファイルを削除するとディレクトリエントリがなくなるため、du はたどる名前を失い、そのファイルをカウントできません。inode とそのブロックは、最後のファイルディスクリプタが閉じるまで割り当て済みのまま残り、df は割り当て済みブロックをカウントします。/proc/<pid>/fd で、ターゲットが deleted と表示されるシンボリックリンクを検索すると、そのファイルと保持しているプロセスの両方を特定できます。この比較を信頼する前に、du を root として、-x 付きで実行したことを確認してください。通常のユーザーでは、読み取り権限のないディレクトリが警告なしにスキップされます。

lsof を使わずに、削除済みで開いたままのファイルを見つけるにはどうすればよいですか?

カーネルが保持している、開いているファイルディスクリプタの記録を使います。sudo find /proc/[0-9]*/fd -lname '*(deleted)' -printf '%p -> %l\n' 2>/dev/null は、名前のないファイルを指すすべてのディスクリプタを一覧表示します。プロセス ID は、表示されるパス内に含まれています。これらのディスクリプタパスのいずれかに対して sudo stat -Lc %s を実行するとサイズを確認できるため、サイズ順に並べて対象を特定できます。パッケージを追加する必要はありません。空き容量のないファイルシステムでは、パッケージのインストール自体が失敗することがあるため重要です。

プロセスを終了せずに空き容量を回復できますか?

場合によっては可能です。sudo truncate -s 0 /proc/<pid>/fd/<n> はディスクリプタを通じて同じ inode にアクセスし、プロセスの実行を継続したままブロックを解放します。プロセスがファイルを append モードで開いていた場合に、これが最も適切です。書き込みが常に現在のファイル末尾へ行われるためです。append モードでなかった場合、書き込みオフセットはその位置に残ります。次の書き込みで先頭に hole があるファイルが再作成されるため、表示サイズは元に戻りますが、hole の下にあるブロックは空きのままです。unit を再起動するか、ドキュメントに記載されたシグナルを送ってログを再オープンさせると、sparse file を残さずに解決できます。

df では空き容量があるのに、書き込みが失敗します。ほかに何が考えられますか?

同じパスに対して df -i を実行し、inode を確認してください。ブロックに空きがあっても inode がなければ、ファイルシステムは新しいファイルを作成できません。ext4 ファイルシステムで予約ブロックだけが残っている状態で、root 以外のユーザーが書き込んでいないかも確認してください。デバイスに対して sudo tune2fs -l を実行すると確認できます。書き込み先のファイルシステムを確認することも重要です。別の /boot または /var/ とは独立して容量を使い果たすためです。

du の合計が df より大きくなるのはなぜですか?

du-x なしで実行すると、指定したパスの下にマウントされたすべてのファイルシステムへ入るため、複数のファイルシステムを合算します。一方、df は 1 つのファイルシステムを示します。bind mount があると、同じファイルが表示される各パスで 1 回ずつカウントされるため、問題はさらに複雑になります。-x を追加して du を 1 つのファイルシステムに限定し、df には同じパスを指定してください。これにより、両方のコマンドが同じ対象を示します。