df -h の出力を1列ずつ解剖(duと食い違う理由も)

ディスク逼迫の一報にも慌てず切り分けられる。df -hの6列をstatfsまで遡って読み解き、duと合わない・SizeがUsed+Availにならない・空きはあるのに書けない、という定番の3つを原因から解消する。

応用コマンド出力Linuxディスク運用最終更新: 2026-07-28
3つの要点
TL;DR
  1. dfはマウント情報を読み、各マウント点にstatfsを発行してスーパーブロックのカウンタを並べる。ファイルを開かないため数百万件でも速く、1行はディスクではなくマウント済みファイルシステム1つに対応する。
  2. dfとduの不一致は、削除済みでもプロセスが開くファイルが主因だ。fdが残る限りブロックは解放されず、dfは数えるがduは辿れない。lsof +L1で保持元を特定し、開き直すと解放される。
  3. SizeとUsed+Availの差はext4が既定で5%確保するroot用予約ブロックで、tune2fs -mで変えられる。Use%の分母はUsed+Avail。空きがあるのに書けないならinode枯渇を疑いdf -iを見る。

このコマンドは何を見せているか

df は disk free の略だが、見せているのは「マウント済みのファイルシステムの空き」であって、ディスクの空きではない。この一語の違いが、本稿のつまずきのほとんどを説明する。

動作は単純だ。カーネルが公開するマウント情報(Linuxでは /proc/self/mountinfo)を読み、並ぶマウント点それぞれに statfs(2) を発行する。返るのはスーパーブロックが持つ集計済みのカウンタ、すなわち総ブロック数・空きブロック数・inode数だ。dfはそれを並べているにすぎない。

つまりdfはファイルを1つも開かず、ディレクトリも歩かない。数百万ファイルでも一瞬で返るのはこのためだ。そして1行はディスクでもパーティションでもなく、マウント済みのファイルシステム1つを指す。

dfに映らない空き

未マウントのパーティション、未フォーマットの領域、LVMの未割り当てエクステントは、dfに一切現れない。ブロックデバイス側の空きは lsblkvgs で見る。

対比を先に置く。dfがカウンタを読むだけなのに対し、du はディレクトリを歩いてファイル1つずつの割り当てブロックを足し上げる。出どころが違う以上、両者がずれるのは異常ではない。

横にスクロール

dfの対象行から容量とinodeの異常を判定し保持元まで調べる手順
Use%だけで決めず、対象マウントのAvail、inode、削除済みファイルの順で切り分ける。

1列ずつ解剖

題材は、メモリ8GB、ルートに50GiBのパーティションを切ったUbuntuのDockerホストである。

$ df -h
Filesystem      Size  Used Avail Use% Mounted on
udev            3.9G     0  3.9G   0% /dev
tmpfs           798M  1.1M  797M   1% /run
/dev/nvme0n1p2   49G   42G  4.5G  91% /
tmpfs           3.9G     0  3.9G   0% /dev/shm
/dev/nvme0n1p1  511M  6.1M  505M   2% /boot/efi
/dev/loop0       64M   64M     0 100% /snap/core20/2105
overlay          49G   42G  4.5G  91% /var/lib/docker/overlay2/3f9c1a.../merged
tmpfs           798M   16K  798M   1% /run/user/1000

6列は、ほぼそのままstatfsのフィールドに対応する。

出どころ意味
Sizef_blocks総ブロック数。ext4はメタデータ分を除く
Usedf_blocks − f_bfree割り当て済みブロック
Availf_bavail非特権プロセスが使える空き
Use%計算値Used ÷ (Used + Avail) の切り上げ
Mounted onマウント情報statfsではなくマウント情報から取る

Filesystem — デバイス名ではなく、マウント元の文字列

ブロックデバイスのパスとは限らず、マウント情報の「マウント元」の文字列が出るだけだ。tmpfsoverlay のような型名も、nfs-server:/export のようなネットワーク上の場所も入る。上の出力では tmpfs が3行あり、どれがどれかはMounted onを見るまで分からない。同じdevtmpfsでも、RHEL系は devtmpfs、Debian系は udev と出る。実際の型は df -T のType列で確かめる。

df -Th                        # 型(Type)列を追加する
df -h -x squashfs -x tmpfs    # 邪魔な行を除く

特殊なファイルシステムの行をどう読むか

tmpfsはRAM上のファイルシステムで、Sizeはマウントオプション size= の値(既定はRAMの半分)にすぎず、その分のメモリが確保済みという意味ではない。使った分だけメモリを食うので、/dev/shm を埋めれば最後はOOM Killerを呼ぶ。/run と /run/user/1000 はsystemd系がRAMの10%で作る798Mの領域だ。

/dev/loop0 はsnapのsquashfsで、Availが0、Use%が100%。読み取り専用の圧縮イメージは作った時点で隙間なく詰まっているのが正常な姿だ。監視では除外する。

overlayの行はコンテナのレイヤを重ねた統合ビューだ。SizeもAvailもルートと同じなのは、overlayfsが自前の容量を持たず、statfsを上位レイヤ(upperdir)が置かれたファイルシステムへ丸投げするからである。コンテナの中で df -h / を見て残り4.5Gと読んだなら、それはホストのルートの残りだ。原理はコンテナのオーバーレイファイルシステムで扱っている。

Size — パーティションの容量ではない

Sizeは f_blocks、ファイルシステムが管理する総ブロック数だ。50GiBのパーティションが49Gと出るのは丸め誤差ではない。ext4はmkfsの時点でinodeテーブル、ジャーナル、ビットマップ、スーパーブロックの複製といったメタデータ領域を確保し、statfsはその分を差し引いた値を返す。この領域は最初からSizeに入っていない。

Used — 見えているファイルの合計ではない

Usedは総ブロック数から空きブロック数を引いた値、つまり割り当て済みのブロック数だ。「ディレクトリから辿れるファイルの合計」ではない。ブロックが誰かに割り当てられていればUsedに乗る。名前がもう無くても、マウントの下に隠れていても、である。この定義がduとの食い違いを生む。

Avail — アプリが実際に書ける量

Availは f_bavail、非特権プロセスが使える空きブロック数だ。空きブロック総数の f_bfree とは別物で、その差が予約ブロックにあたる。ext4は既定でroot専用に総ブロックの5%を予約しており、手を出せるのはrootか、tune2fs -u-g で指定したUID/GIDのプロセスだけだ。だからSizeからUsedとAvailを引くと、ちょうど予約分が残る。運用で見るべきはこの列である。

Use% — 分母はSizeではない

最も誤解される列だ。Use%は Used ÷ Size ではなく、Used ÷ (Used + Avail) を切り上げた値である。ルートの行なら 42 ÷ (42 + 4.5) = 90.3% で、切り上げて91%。Size(49G)を分母にすれば86%で、表示と合わない。帰結として、Availが0になった瞬間にUse%は100%になる。予約ブロックが残っていても、非特権プロセスから見れば1バイトも書けないのだから正しい表示だ。

監視するならUse%よりAvail

Use%は分母がUsed+Availなので、tune2fs -m で予約を減らすだけで、1バイトも消していないのに値が下がる。100TBの1%と10GBの1%も意味がまったく違う。閾値監視はAvailの絶対値と増加の傾きで組むほうが確実だ。

Mounted on — 一致するのは最も深いマウント

マウント点のパスで、statfsではなくマウント情報から取る。押さえるべきは、あるパスへの書き込みがどの行に支配されるかだ。答えは、そのパスを含む最も深いマウント点である。/var/log がdfに現れないなら独立したファイルシステムではなく、ルートの行のAvailが/var/logの運命を握っている。目で探すより、パスを引数に渡すのが速い。

df -h /var/lib/postgresql/data   # そのパスを含むFSの行だけが出る

つまずきやすい点

第一に、dfとduが合わない。まず作法から。duは既定でマウント境界を越えて歩くので、dfの1行と比べるなら -x で1つのファイルシステムに閉じ込め、読めないディレクトリで過少集計にならないようroot権限で実行する。

du -shx /     # ルートと同じFSだけを合計する
df -h /

原因はほぼ2つだ。1つ目は、削除済みなのに開かれたままのファイル。unlink が消すのはディレクトリエントリ、つまり名前だけである。inodeには別に参照カウンタがあり、プロセスがfdを握る限りデータブロックは解放されない。名前が無いのでduは辿り着けず、ブロックは割り当てられたままなのでdfは数える。ローテートでログを消したのにアプリが古いfdを掴み続けている、が典型例だ(fdとinodeの関係はファイルディスクリプタとオープンファイルテーブルの構造に詳しい)。リンク数が1未満のファイルを一覧する lsof +L1 で犯人が分かる。

$ lsof +L1
COMMAND  PID USER  FD  TYPE DEVICE   SIZE/OFF NLINK   NODE NAME
java    1287  app   3w  REG  259,2 8589934592     0 262147 /var/log/app/app.log (deleted)

NLINKが0、サイズは8GiB。これがdfにだけ見えていた正体だ。解放するにはプロセスにファイルを開き直させる。再起動できないなら、/proc経由でfdの実体を切り詰める手もある。

systemctl restart app          # 本筋: 開き直させる
truncate -s 0 /proc/1287/fd/3  # 応急: fd経由で中身を捨てる

2つ目は、マウントで隠れたディレクトリ。/mnt/data にファイルを置いた後でそこにディスクをマウントすると、元のファイルはルート上に残り続けるのにパスからは見えなくなる。duはどう歩いても届かず、ルートのUsedだけが減らない。バインドマウントは再帰的でないため、重ねる前の素のツリーを覗ける。

mount --bind / /mnt/rootview   # 重ねる前のルートが見える
du -shx /mnt/rootview/mnt
umount /mnt/rootview

第二に、SizeがUsed + Availと一致しない。ルートの行では 49 − 42 − 4.5 = 2.5G が行方不明に見える。これがext4の予約ブロックで、mke2fsが既定で総ブロックの5%をroot用に確保している。ディスクが埋まってもroot権限のデーモンは書き込めるようにして復旧手段を残すことと、アロケータに余地を残して断片化を抑えることが目的だ。変更にアンマウントも再フォーマットも要らない。

tune2fs -l /dev/nvme0n1p2 | grep -i reserved
tune2fs -m 1 /dev/nvme0n1p2   # 予約を1%へ下げる

4TBのボリュームなら5%は200GBに達するので、データ置き場と割り切れるなら1%まで下げる価値はある。ただしルートで0%にするのは避けたい。満杯の瞬間にrootでも書けなくなる。

第三に、容量は空いているのに書けない。Availは4.5Gあるのに、touchすら No space left on device で失敗する。ファイルシステムはブロックとinodeを別々に管理しており、ブロックが余っていてもinodeが尽きればファイルは作れないからだ。

$ df -i /
Filesystem       Inodes   IUsed IFree IUse% Mounted on
/dev/nvme0n1p2  3276800 3275712  1088  100% /

ext4のinode数はmkfsの時点で固定される。既定はおよそ16KBに1個の割合なので、50GiBなら約327万個。後から増やせず、ファイルシステムごと拡張する以外に道はない。極小ファイルを数百万個作る用途では、容量よりinodeが先に尽きる。XFSはinodeを動的に割り当てるため、この形の枯渇は基本的にext系の問題だ。

du --inodes -xd1 /var | sort -n | tail   # 直下のどこがinodeを食っているか

厄介なのは、この状況でもerrnoが容量不足と同じENOSPCになる点だ。メッセージだけ見て容量を疑い、dfで空きを確認して混乱する。空きがあるのに書けないと感じたら反射的に df -i を打ちたい(ENOSPC(No space left on device)に逆引きをまとめてある)。

まとめ

dfは、マウント情報を読んで各ファイルシステムにstatfsを問い合わせ、カウンタを並べているだけのコマンドだ。この一点を押さえれば6列の癖は説明がつく。Sizeが小さいのはメタデータを引いているから、Used + Availにならないのは予約ブロックがあるから、Use%が100%でも空きが残るのは分母がSizeではないから、duと食い違うのは出どころが違うからである。

障害対応の型はこうだ。df -h で当たりを付け、空きがあるのに書けないなら df -i、duと合わないなら lsof +L1。この3手で、ディスク満杯の大半は原因まで辿り着ける。

コマンド出力の読み方の記事ガイド

df -h の出力を1列ずつ解剖(duと食い違う理由も)を実務で読む

TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。

解決すること

コマンド出力

比較で見る軸

難易度: advanced / カテゴリ: コマンド出力の読み方 / タグ数: 4

導入後に効く点

dfとduの不一致は、削除済みでもプロセスが開くファイルが主因だ。fdが残る限りブロックは解放されず、dfは数えるがduは辿れない。lsof +L1で保持元を特定し、開き直すと解放される。

先に潰すリスク

用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。

数字・仕様の読み方
難易度
advanced
カテゴリ
コマンド出力の読み方
タグ数
4

判断チェックリスト

  • 自社の用途が「コマンド出力 / Linux」に近いか確認する。
  • 強みである「dfはマウント情報を読み、各マウント点にstatfsを発行してスーパーブロックのカウンタを並べる。ファイルを開かないため数百万件でも速く、1行はディスクではなくマウント済みファイルシステム1つに対応する。」が本当に評価軸になるか確認する。
  • 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
  • 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
  • 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
  • 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。

次に確認する観点

コマンド出力Linuxディスク運用