No space left on deviceの対処|4原因の切り分け

No space left on deviceの対処|4原因の切り分け

【PR】この記事にはアフィリエイトリンク(ConoHa VPS)が含まれます。

サイトが真っ白になった。SSHで入ったら、何を打っても同じエラーが返ってくる。

-bash: /home/michi/.bash_history: No space left on device

ディスクが満杯です。この状態になると、ログが書けない・セッションが保存できない・データベースが書き込みに失敗するという形で、サービスが連鎖的に止まります。MySQL が read-only になったり、apt すら動かなくなったりもします。

厄介なのは、df -h を見て「空きがある」と表示されているのに書けないケース(inode 枯渇)があることです。「不要ファイルを消しましょう」で終わっている記事を読んで消しても、df -h の数字が1バイトも動かない——これも、原因を取り違えているとよく起きます。

この記事では、原因を4つに切り分けて、それぞれの見つけ方と安全な空け方を書きます。まず、いまのあなたのケースがどれかを特定してください。

この記事の結論(まずこの4つで切り分ける)
  • ① ふつうに使い切っているdf -h が 100%。du で犯人のディレクトリを上から辿る
  • ② inode 枯渇df -h に空きがあるのに書けない。df -iIUse% が 100% なら確定。小さいファイルの大量生成が原因
  • ③ 削除済みなのにプロセスが掴んだままrm したのに df が動かない。sudo lsof -nP +L1 で発見。プロセスの再起動か truncate で解放する
  • ④ ログが太り続けている → 消してもすぐ再発する。journalctl --disk-usage と logrotate の設計を見る

そして全パターン共通の鉄則:消す前に「何を消すのか」を必ず表示して確認するrm -rf を思いつきで打つのが、この作業でいちばん危険です。

No space left on device の対処を4原因で切り分けるフロー図。df -h の使用率、df -i のinode枯渇、lsof +L1 の削除済みファイル、journalctl --disk-usage のログ肥大の順に判定する
上から3コマンド打つだけで、たいていはこの4つのどれかに落ちる

私自身、ConoHa VPS で WordPress とツール群を動かしていて、この4つは全部踏みました。とくに③は、rm して「消したのに空かない」と30分ほど固まったことがあります。原因を知っていれば1分で終わる話でした。

なお、動作確認は Ubuntu 24.04 LTS の実機で行い、コマンドの仕様は Ubuntu 26.04(resolute)の man ページで照合しています(2026年8月25日時点)。ファイルシステムは ext4 前提です。

目次

まず応急処置:いま数百MBを安全に取り戻す

原因調査の前に、そもそもコマンドを打つ余裕すら無いことがあります。エディタが保存できない、apt がテンポラリを作れない、といった状態です。その場合は、次の3つだけ先にやってください。どれも「消えて困るデータ」には触りません。

# ① systemd のログを直近200MBまで削る(アーカイブ済みのみが対象)
sudo journalctl --rotate
sudo journalctl --vacuum-size=200M

# ② ダウンロード済みのパッケージキャッシュを空にする(再ダウンロードすれば戻る)
sudo du -sh /var/cache/apt/archives
sudo apt clean

# ③ 空いた分を確認
df -h /

journalctl --rotate を先に打っているのには理由があります。--vacuum-size= はアーカイブ済みのジャーナルファイルしか削らないので、いま書き込み中のファイルが大きいと、vacuum しても --disk-usage がほとんど減りません(journalctl の man に明記されています)。--rotate で現行ファイルをアーカイブに回してから vacuum すると、狙いどおり減ります。私が最初にハマったのはここです。

⚠️ この作業で絶対にやってはいけないこと
  • sudo rm -rf /var/log/* … ログのディレクトリごと消えます。/var/log/nginx/ などが無くなると、そのサービスは以後ログを書けず、再起動するまで(場合によっては手でディレクトリを作り直すまで)復旧しません。ログを空にしたいなら消すのではなく truncate -s 0 を使います
  • sudo rm -rf /tmp/* … 稼働中のプロセスが /tmp にソケットや作業ファイルを置いていることがあります(MySQL のソケット等)。中身を確認せずに消さないでください
  • sudo rm/var/lib/mysql 配下のファイルを消す … binlog を含め、データベースのファイルを直接消すと復旧できません。後述の SQL コマンドで消します
  • 消す対象をワイルドカードで指定して、ls で確認せずに実行する … 打ち間違いは戻せません

応急処置で息継ぎができたら、原因の特定に移ります。ここから先は、上から順に読む必要はありません。図解01のフローで自分のケースまで飛んでください。

原因①:ふつうに使い切っている(df -hdu で犯人を辿る)

いちばん多いケースです。ただし、ここでも「どこを見るか」の順番を間違えると遠回りになります。

まず「どのファイルシステムが」満杯かを確認する

df -h は全部のファイルシステムを出します。/ が満杯なのか、/boot なのか、/var が別パーティションなのか、/run(tmpfs)なのかで対処がまるで違います。

df -hT

-T を付けると ext4 / tmpfs / vfat といったファイルシステムの種類も出ます。見るべきは Use% が 100%(または 99%)の行です。

特定のパスがどのファイルシステムに属するかを知りたいときは findmnt が便利です。

findmnt -T /var/log

よくある勘違いが3つあります。

  • /boot が満杯:カーネルの更新が溜まっているだけで、/ には余裕があることが多い(後述)
  • /run が満杯:tmpfs(メモリ上)です。ディスクを空けても解決しません。/run に大量に書いているプロセスを探します
  • / に見えて実は別マウント:ConoHa VPS の追加ディスクなどを /var にマウントしていると、df -h / だけ見ても気づけません

du で上から辿る(-x を付ける理由)

満杯のファイルシステムが分かったら、du で階層を降りていきます。よく紹介されるのは sudo du -sh /* | sort -h ですが、この書き方は /proc/sys にも降りていくため、遅いうえに数字がノイズだらけになります

# -x:他のファイルシステムに降りない(/proc /sys /dev /run が自動的に除外される)
sudo du -xh -d 1 / | sort -h

出力の下のほうに、いちばん太っているディレクトリが並びます。そこを起点に、同じコマンドで1段ずつ降ります。

sudo du -xh -d 1 /var | sort -h
sudo du -xh -d 1 /var/log | sort -h

対話的に探したいなら ncdu が圧倒的に速いです(矢印キーで階層を歩けます)。

sudo apt install ncdu
sudo ncdu -x /

大きいファイルを直接探すこともできます。

# 100MB を超えるファイルを、大きい順に20件
sudo find / -xdev -type f -size +100M -printf '%s\t%p\n' 2>/dev/null \
  | sort -rn | head -20 | numfmt --to=iec --field=1

-xdevdu -x と同じ役割(他のファイルシステムに降りない)、numfmt --to=iec がバイト数を 1.2G のような表記に直してくれる部分です。

容量を食っている犯人になりやすい場所

私の経験と、実際に相談を受けた範囲でいうと、だいたいこのあたりです。

/var/log

Nginx / Apache のアクセスログ、アプリのログ。ローテーションが効いていないと単独で数十GBまで育ちます(→ 原因④)

/var/log/journal

systemd のジャーナル。既定でファイルシステムの10%(上限4GB)まで使ってよい設定なので、放っておくと4GB近くまで太ります(→ 原因④)

/var/lib/docker

使っていないイメージ・停止済みコンテナ・ボリューム、そしてコンテナのログ(既定で上限なし)

/var/cache/apt/archives

ダウンロード済みの .debsudo apt clean で空にできます

/boot

古いカーネル。/boot が別パーティションだと、ここだけ先に満杯になります

/var/lib/snapd/snaps

snap の旧リビジョン。1パッケージで数百MB単位

/var/lib/mysql

データ本体のほかに binlog(バイナリログ)。設定によっては際限なく増えます

WordPress の wp-content

uploads の画像と、バックアッププラグインが作った圧縮ファイル。私は WPvivid の世代が溜まって数GB食っていたことがあります

/swapfile

swap ファイルもディスクを消費します(swap の設計そのものは別記事の担当です)

消す前に「何を消すのか」を必ず表示する

ここが本記事でいちばん強調したいところです。削除コマンドは、まず「消す対象を一覧するだけ」の形で1回実行してください。

# ❌ いきなりこれを打たない
# sudo find /var/log -name '*.gz' -mtime +30 -delete

# ✅ ①まず何件・どれが対象かを見る
sudo find /var/log -name '*.gz' -mtime +30 | wc -l
sudo find /var/log -name '*.gz' -mtime +30 | head -20

# ✅ ②合計サイズを見る(消して何GB空くのかを先に知る)
sudo find /var/log -name '*.gz' -mtime +30 -printf '%s\n' | awk '{s+=$1} END {print s/1024/1024" MB"}'

# ✅ ③納得してから消す
sudo find /var/log -name '*.gz' -mtime +30 -delete

3行余分に打つだけで、本番サーバーを飛ばす事故はほぼ防げます。とくに find-name の書き間違い1文字で対象が激変するので、-delete を付ける前に必ず | head で目視してください。

🔴 -delete を書く位置にも注意してください。find はコマンドラインを式として左から評価するため、-delete を条件より先に書くと、起点ディレクトリ配下を全部消しにいきますsudo find /var/log -delete -name '*.gz'/var/log の全消しです。man が名指しで警告している罠なので、-delete は必ず最後に置いてください。 なお上の例に -type f を足しておくと、ディレクトリにマッチする事故も防げます(sudo find /var/log -type f -name '*.gz' -mtime +30 -delete)。

古いカーネルと apt キャッシュを片付ける(/boot 対策)

/boot が満杯の場合は、まずいま動いているカーネルを確認します。これを消すと起動しなくなります。

uname -r                          # 稼働中のカーネル(絶対に消さない)
dpkg -l 'linux-image-*' | grep ^ii # インストール済みの一覧

Ubuntu は不要になったカーネルを自動判定できるので、手で rm せず apt に任せます。

sudo apt autoremove --purge

/boot が満杯で apt 自体が失敗する場合は、先に古い initrd を1つだけ手動で消して隙間を作り、それから apt autoremove --purge を回します。ここは書き間違えると次に起動しなくなる場所なので、必ず一覧してから消してください。

uname -r                                   # 稼働中のカーネル。この文字列を含むファイルは絶対に消さない
ls -lh /boot/initrd.img-* /boot/vmlinuz-*  # 実際に何があるかを一覧する

# 上の一覧のうち、uname -r と一致しないバージョンの initrd を1つだけ消す(例)
sudo rm /boot/initrd.img-6.8.0-31-generic

sudo apt autoremove --purge                # 隙間ができたら、残りは apt に任せる

⚠️ Ubuntu の initrd は /boot/initrd.img-<バージョン> という名前で、拡張子は付きません(24.04 以降の圧縮は zstd です)。/boot/*.gz を探しても目的のファイルは見つかりません。また apt autoremove --purge はカーネル以外も削除候補にするので、表示される一覧を必ず読んでから y を押してください。

df の「100%」は本当の100%ではない(ext4 の予約ブロック)

df -h の出力をよく見ると、Size から Used を引いても Avail にならないことに気づきます。

Filesystem      Size  Used Avail Use% Mounted on
/dev/vda1        25G   24G     0 100% /

Size から Used を引いた分がどこにも出てきません。これは ext4 が既定でファイルシステムの5%を root 専用に予約しているためです(mke2fs の man に「The default percentage is 5%.」と明記されています)。25GB のファイルシステムなら 約1.2GB が予約分です。この予約は、ディスクが埋まってもシステムが最低限動けるようにするための保険で、一般ユーザーには「空きゼロ」として見えますが root は書き込めます。

なお df -h は表示を丸めるので、SizeUsed の引き算では正確な予約量は出ません。正確な値は次のコマンドで確認します。

findmnt -no SOURCE /                                  # デバイス名を確認(例:/dev/vda1)
sudo tune2fs -l /dev/vda1 | grep -iE 'block count|reserved block count|block size'

Reserved block count × Block size が予約バイト数です。

df -h が100%でもroot予約分が残っている図。25GBのext4では既定5%(約1.2GB)がroot専用に予約され、一般ユーザーにはAvail 0と表示される
Size − Used ≠ Avail の差が root 予約分。ここを削るのは最後の手段

この予約は減らせます。

sudo tune2fs -m 1 /dev/vda1     # 5% → 1% に変更(マウントしたままでも実行可)
df -h /                          # Avail が増えることを確認

⚠️ ただし、これは応急処置であって解決ではありません。 予約ブロックには「root がログインして復旧作業できる余地を残す」「断片化を抑える」という役割があります。-m 0 にするのは避けてください。 大容量ディスクなら 1〜2% でも実害は出にくいですが、根本原因を消していないので、また同じところに戻ります。

原因②:df -h に空きがあるのに No space left on device(inode 枯渇)

df -h は 60% しか使っていないのに No space left on device が出る」——これが典型的な inode 枯渇です。ここを扱っている記事は多くありません。そして df -i を見ない限り、原因にはたどり着けません。

df -i で判定する

df -i
Filesystem      Inodes   IUsed  IFree IUse% Mounted on
/dev/vda1      1638400 1638400      0  100% /

IUse% が 100%(IFree が 0)なら確定です。ブロック(容量)は空いているのに、ファイルを1つも新規作成できない状態です。

inode枯渇の仕組みの図。ディスク容量に空きがあってもinodeを使い切ると新しいファイルを作れず No space left on device になる
ディスクには「容量」と「inode」の2つの在庫がある。どちらかが尽きたら書けない

inode はファイル1個・ディレクトリ1個につき1つ消費されます。サイズは関係ありません。0バイトのファイルでも1つ使います。つまり「小さいファイルが大量に生成される」パターンで枯渇します。

どのディレクトリが inode を食っているかを特定する

du にはファイル数を数えるモードがあります。容量ではなく個数で犯人を辿れます。

# 容量ではなく「ファイル数」で上から辿る
sudo du -x --inodes -d 1 / | sort -rn | head -20
sudo du -x --inodes -d 1 /var | sort -rn | head -20

--inodes が使えない古い環境なら、find で親ディレクトリごとに数える手もあります。

sudo find / -xdev -printf '%h\n' 2>/dev/null | sort | uniq -c | sort -rn | head -20

左の数字がファイル数、右がディレクトリです。10万・100万といった桁が並んだ場所が犯人です。

定番の原因はだいたい決まっている

PHP のセッションファイル(/var/lib/php/sessions

アクセスのあるサイトで、セッションの掃除が止まると一気に増えます。Debian/Ubuntu の PHP パッケージには定期削除の仕組みが入っているので、まず動いているか確認します(systemctl list-timers 'phpsession*'

メールキュー(/var/spool/postfix/

送信できないメールが溜まり続けるパターン。1通ごとにファイルなので、スパム送信の踏み台にされたときは爆発的に増えます

アプリのキャッシュ・一時ファイル

小さいファイルを大量に作る設計のフレームワークやビルドツール。node_modules も1つで数万ファイルあります

/tmp

cron で回しているスクリプトが一時ファイルを消し忘れているケース

cron のメール(/var/mail/ /var/spool/mail/

cron ジョブが標準出力に何か出すたびにメールが積まれます

大量の小さいファイルを安全に消す

ここでも「消す前に確認」です。そして、rm は数十万ファイルを渡すと引数の上限を超えて失敗します

-bash: /usr/bin/rm: Argument list too long

find-delete を使えばこの問題は起きません。

# ① 何件あるか(時間がかかるので覚悟して待つ)
sudo find /var/lib/php/sessions -type f -mtime +1 | wc -l

# ② 実際に何が対象か目視
sudo find /var/lib/php/sessions -type f -mtime +1 | head -20

# ③ 納得したら削除
sudo find /var/lib/php/sessions -type f -mtime +1 -delete

# ④ inode が戻ったか確認
df -i /

-mtime +1 のような条件を必ず入れるのがポイントです。なお find は端数を切り捨てるので、-mtime +1 は「1日より前」ではなく「まる2日以上更新されていないもの」になります(man に明記されています)。保持期間を計算するときはここがずれやすいので注意してください。条件なしで全部消すと、いまログイン中のユーザーのセッションまで飛びます

ext4 は後から inode を増やせない

これは知っておいてください。ext4 の inode 数はファイルシステム作成時に決まり、あとから増やせませんmke2fs の man に「it is not possible to change this ratio on a file system after it is created」と書かれています)。resize2fs でファイルシステムを拡張すれば比例して増えますが、既存のパーティションで inode だけ増やすことはできません。

つまり inode 枯渇の恒久対策は「小さいファイルを溜めない運用」しかありません。定期削除の仕組み(cron・systemd timer・アプリ側の設定)を直すのが正解です。

なお、RHEL系(AlmaLinux など)の既定ファイルシステムである XFS は inode を動的に確保するため、この問題は起きにくいです。ただしゼロではありません。RHEL系の初期設定はAlmaLinux VPSの初期設定に分けてあります。

番外:容量も inode も空いているのに ENOSPC が出る(inotify の監視上限)

4原因のどれにも当てはまらないのに No space left on deviceENOSPC)が返る、という珍しいケースがあります。ファイル変更を監視する inotify の監視数(watch)の上限に達したときです。ビルドツールやファイル同期ツール、監視系のエージェントを動かしていると起きます。

エラーの出方が紛らわしく、inotify_add_watch ... : No space left on device のようにディスクとは無関係の文脈で出ます。df -hdf -i も余裕たっぷりなのが特徴です。

cat /proc/sys/fs/inotify/max_user_watches     # 現在の上限
cat /proc/sys/fs/inotify/max_user_instances   # インスタンス数の上限

上限を引き上げる場合はこうします。

sudo tee /etc/sysctl.d/99-inotify.conf >/dev/null <<'EOF'
fs.inotify.max_user_watches = 524288
EOF
sudo sysctl --system
sysctl fs.inotify.max_user_watches            # 反映確認

/etc/sysctl.d/systemd と同じく「後に読まれたファイルが勝つ」ので、番号は 99- のように大きいものを付けます。監視数はカーネルメモリを消費するため、必要以上に大きくしないでください。

原因③:rm したのに空かない(削除済みだがプロセスが掴んでいる)

巨大なログを rm したのに df -h の数字が1バイトも動かない。 これが3つ目のパターンで、原理を知らないと永遠に解決しません。

Linux では、ファイルを rm してもディスク領域はすぐには解放されません。解放されるのは「ディレクトリからのリンクが0になり、かつ、そのファイルを開いているプロセスが1つも無くなったとき」です。Nginx や MySQL のように起動時にログを開きっぱなしにするプロセスは、削除後もファイルの実体を握り続けます。

rmしたのに容量が空かない仕組みの図。リンク数がゼロでもプロセスがファイルを開いたままなら領域は解放されず、df -h の数字が動かない
rm は「名前を消す」だけ。掴んでいるプロセスがいる限り実体は残る

lsof +L1 で見つける

sudo lsof -nP +L1

+L1 は「リンク数が1未満(=0)のファイルを表示する」という指定です。つまり削除済みなのに開かれたままのファイルだけが出ます。

COMMAND   PID  USER   FD   TYPE DEVICE  SIZE/OFF NLINK    NODE NAME
nginx    1024  www-d    5w   REG  253,1 8589934592     0  262145 /var/log/nginx/access.log (deleted)

見るのは SIZE/OFF(実体のサイズ)、PIDFDNAME の4つです。この例では nginx(PID 1024)がファイルディスクリプタ 5 で 8GB のファイルを掴んでいます。

💡 lsof は入っていないことがあります(VPS のミニマルなイメージでは未インストールのことがあります)。sudo apt install lsof で入りますが、ディスクが満杯だと apt が失敗する可能性があります。その場合は /proc から直接探せます。

sudo find /proc/[0-9]*/fd -ls 2>/dev/null | grep '(deleted)'

出力の /proc/1024/fd/5 -> /var/log/nginx/access.log (deleted) という部分から、PID とファイルディスクリプタ番号が読めます。

解放する2つの方法

方法A:掴んでいるプロセスを再起動する(安全・推奨)

いちばん確実です。サービスによっては、再起動せずログを開き直すだけのコマンドがあります。

sudo nginx -s reopen          # Nginx:ログファイルを開き直す(無停止。公式が USR1 として定義している操作)
# または(サービス自体を入れ替える)
sudo systemctl restart nginx  # 再起動すれば確実に解放される(一瞬の停止あり)

df -h /                       # 解放されたか確認

方法B:/proc 経由で中身を切り詰める(再起動できないとき)

サービスを止められない場合は、プロセスが握っているファイルの中身だけを0にします。

# lsof で見つけた PID と FD 番号を使う(例:PID 1024 / FD 5)
sudo truncate -s 0 /proc/1024/fd/5
df -h /

🔴 これはログファイルにだけ使ってください。 データベースのデータファイルや、アプリが読み書き中の一時ファイルに対してやると、中身が消えて復旧できませんlsofNAME 列を見て、それが本当にログファイルであることを確認してから実行してください。

そもそも rm ではなく truncate を使う

稼働中のログを空けたいときは、最初から rm を使わないのが正解です。

# ❌ rm すると原因③になる
# sudo rm /var/log/nginx/access.log

# ✅ 中身を空にする(ファイル自体は残るのでプロセスも困らない)
sudo truncate -s 0 /var/log/nginx/access.log

truncate -s 0 なら inode もファイルディスクリプタもそのままなので、この問題は起きません。

dudf の合計が合わない(もう1つの理由:マウントポイントの下に隠れたファイル)

du で足し合わせても dfUsed に届かない、というときの原因は2つあります。

  1. 削除済みだがプロセスが掴んでいるファイル(この章の内容)。du はディレクトリを歩くので、名前が無いファイルは数えられません
  2. マウントポイントの下に隠れたファイル。あるディレクトリにファイルを書いたあとで、そこに別のディスクをマウントすると、元のファイルは見えなくなりますが容量は消費し続けます

2 を確認するには、ルートを別の場所にバインドマウントして覗きます。

sudo mkdir -p /mnt/rootfs
sudo mount --bind / /mnt/rootfs
sudo du -shx /mnt/rootfs/* | sort -h      # 隠れていたファイルもここでは見える
sudo umount /mnt/rootfs

見終わったら umount を忘れずに。バインドマウントを残したまま du を回すと二重計上になります。

原因④:消してもすぐ再発する(journald と logrotate のログ肥大)

①〜③で空けても、数日でまた満杯になる。そのときは、原因ではなく「設計」を直す番です。Ubuntu のログは2系統あり、片方だけ直しても止まりません。

  • systemd の journal/var/log/journal)… journalctl で読むログ。logrotate の管理外
  • テキストのログファイル/var/log/nginx/*.log など)… logrotate が管理

journald の使用量を見る・削る

journalctl --disk-usage

Archived and active journals take up 3.9G in the file system. のように出ます。journald の既定は「ファイルシステムの10%まで(ただし上限4GB)」で、さらに「空きが15%(上限4GB)を切らないようにする」という条件が付きます(journald.conf(5))。つまり放っておいても4GB近くまでは太る設計です。

その場で削るには vacuum を使います。アーカイブ済みしか対象にならないので、--rotate を先に打つのがコツでした。

sudo journalctl --rotate
sudo journalctl --vacuum-size=200M     # 合計200MBまで減らす
# あるいは期間で
sudo journalctl --vacuum-time=7d       # 7日より古いものを削除

journalctl --disk-usage                # 反映確認

恒久対策:ドロップインで上限を決める(★番号の付け方に注意)

毎回手で削るのは現実的ではないので、上限を設定します。/etc/systemd/journald.conf を直接編集せず、ドロップインで足すのが今どきの作法です。

sudo mkdir -p /etc/systemd/journald.conf.d
sudo tee /etc/systemd/journald.conf.d/99-size.conf >/dev/null <<'EOF'
[Journal]
SystemMaxUse=500M
SystemKeepFree=1G
EOF

sudo systemctl restart systemd-journald
⚠️ ハマりどころ:systemd のドロップインは「後勝ち」。sshd とは逆

このカテゴリで何度も書いているとおり、sshd_config のドロップインは「先に読まれたファイルが勝つ」ので、/etc/ssh/sshd_config.d/ では 00- のような若い番号を付けるのが正解です(99- だと 50-cloud-init.conf に先を越されて設定が無効化されます)。

ところが systemd の *.conf.d/ は逆です。 systemd-system.conf(5) の man にはこう書かれています——「When multiple files specify the same option, for options which accept just a single value, the entry in the file sorted last takes precedence」。つまり journald.conf.d/ では大きい番号(99-)を付けたファイルが勝ちます

私はここで一度ハマりました。sshd の癖で 00-size.conf を作ったところ、後から入った別のドロップインに上書きされて、SystemMaxUse が効いていませんでした。同じ「ドロップイン」でも、ソフトウェアによって勝ち負けのルールが違います。

反映は必ずコマンドで確認してください。「設定したつもり」がいちばん危険です。

# 最終的に採用された設定を、ドロップインも含めて表示する
systemd-analyze cat-config systemd/journald.conf | grep -iE 'SystemMaxUse|SystemKeepFree'

# 実際の使用量が下がったか
journalctl --disk-usage

logrotate が効いているか確認する

テキストのログ側です。Ubuntu では systemd のタイマーで日次実行される構成が標準なので、まずタイマーが生きているかを見ます。

systemctl list-timers 'logrotate*'
systemctl status logrotate.service --no-pager   # 前回の実行結果とエラー

設定が意図どおり動くかは、変更を加えないデバッグ実行で確かめられます。

sudo logrotate -d /etc/logrotate.conf

-d は man に「no changes are made to the logs and the logrotate state file is not updated」と明記された安全なモードです。出力に rotating log /var/log/nginx/access.log のような行が出るか、log does not need rotating で止まっているかを読みます。

前回いつローテーションしたかは、ステートファイルに残っています。

sudo grep nginx /var/lib/logrotate/status

日付が何か月も前で止まっていたら、そのログはローテーションされていません

# 強制的に1回ローテーションする(-d を外すので実際に変更が入る点に注意)
sudo logrotate -v -f /etc/logrotate.conf

logrotate の3大ハマりどころ

設定ファイルのパーミッションで丸ごと無視される

logrotate は man に「For security reasons configuration files must not be group-writable nor world-writable.」と書かれているとおり、グループ・その他に書き込み権があるファイルを読みません。自分で /etc/logrotate.d/myapp を置いたのに効かないときは、まずここを疑います(sudo chmod 644 +所有者 root)

アプリがログを開き直さない

logrotate の既定はファイルをリネームする方式です。アプリ側が開き直してくれないと、リネームされた(あるいは削除された)ファイルを掴み続け、そのまま原因③になります。設定に postrotate でシグナルを送る記述があるか確認してください(Nginx なら nginx -s reopen

copytruncate の副作用

開き直せないアプリのために「コピーしてから元ファイルを切り詰める」方式が用意されていますが、man にも「there is a very small time slice between copying the file and truncating it, so some logging data might be lost」とあるとおり、ごく短時間ぶんのログが落ちる可能性があります。使えるが万能ではない、という理解で

Docker・snap・MySQL の「増え続ける」を止める

ログ以外にも、放置すると増え続けるものがあります。

Docker:まず現状を見ます。

docker system df                # イメージ・コンテナ・ボリュームの内訳
docker system df -v             # 詳細(どのイメージが何GBか)
docker image prune              # 使われていないイメージだけ削除

🔴 docker system prune -a --volumes は使わないでください(内容を理解している場合を除く)。 --volumesどのコンテナからも使われていないボリュームを全部消します。ディスク満杯でDBコンテナが停止している今この瞬間に打つと、DBの永続データが「未使用」と判定されて消えます。まず docker volume ls で何があるか確認してから、消すものを個別に指定するのが安全です。

コンテナのログは既定で上限がありません/etc/docker/daemon.json に上限を書いて Docker を再起動します(この設定が効くのは、以後新しく作られるコンテナからです。既存のコンテナは作り直しが必要)。

{
  "log-driver": "json-file",
  "log-opts": { "max-size": "10m", "max-file": "3" }
}

snap:無効化された旧リビジョンが残ります。

snap list --all | grep disabled                     # まず対象を目視
sudo snap set system refresh.retain=2               # 保持する版数を減らす
snap get system refresh.retain                      # 反映確認

指定できる範囲は snapd の版で変わるので、必ず snap get で反映を確認してください。

MySQL / MariaDB の binlogファイルを直接 rm してはいけません(インデックスと不整合になります)。SQL で消します。

SHOW BINARY LOGS;                                   -- まず一覧を確認
PURGE BINARY LOGS BEFORE '2026-08-01 00:00:00';     -- 期日より前を削除

恒久的には binlog_expire_logs_seconds を設定して自動削除にします。レプリケーションを組んでいる場合は、スレーブが読み終わっていない binlog を消すと復旧不能になるので、構成を理解したうえで実施してください。

空けたあとにやること(サービスの復旧確認)

容量が戻っても、満杯の間に壊れたサービスは自動では戻りません。最後に必ず確認します。

systemctl --failed                       # 落ちているサービスの一覧
sudo systemctl restart mysql             # 例:書き込み失敗で不調になったもの
sudo journalctl -p err -b --no-pager | tail -50   # 今回の起動でのエラーだけ

MySQL は容量不足でテーブルが read-only 相当になったり、クラッシュリカバリが必要になったりします。WordPress を動かしているなら、管理画面にログインして投稿の保存が通るところまで確認してください。

二度と満杯にしないための予防

原因を潰したら、次は「気づける状態」を作ります。難しいことはしません。

週1回、使用率をメールか通知で受け取る

cron でしきい値を超えたときだけ通知する、が最小構成です。

sudo tee /usr/local/bin/disk-alert.sh >/dev/null <<'EOF'
#!/bin/bash
THRESHOLD=80
USAGE=$(df -P / | awk 'NR==2 {gsub("%","",$5); print $5}')
IUSAGE=$(df -Pi / | awk 'NR==2 {gsub("%","",$5); print $5}')
if [ "$USAGE" -ge "$THRESHOLD" ] || [ "$IUSAGE" -ge "$THRESHOLD" ]; then
  echo "disk ${USAGE}% / inode ${IUSAGE}% on $(hostname)"
fi
EOF
sudo chmod 755 /usr/local/bin/disk-alert.sh
sudo /usr/local/bin/disk-alert.sh        # 手で1回実行して動作確認

df -P は出力を1行に固定するオプションで、awk でのパースが安定します。inode 側(df -Pi)も一緒に見ているのがポイントです。あとは cron に登録し、出力をメールで受け取るか、通知サービスに投げます。

sudo crontab -e
# 毎日9時に実行(何も出力しない日はメールも飛ばない)
0 9 * * * /usr/local/bin/disk-alert.sh

⚠️ cron のメールは MTA(Postfix 等)が入っていないと届きません。VPS の素の Ubuntu には MTA が無いので、このままでは「通知を仕込んだつもりで届かない」状態になります。sudo apt install -y mailutils で最小構成を入れるか、スクリプトの echo を Slack / Discord の Webhook へ curl する形に差し替えてください。仕込んだ直後に必ず1回、THRESHOLD=0 にして通知が届くかテストすること。

しきい値は80%で鳴らす

95% で鳴らしても手遅れです。80% で気づいて、90% になる前に手を打つ。ディスクは埋まる直前に加速することが多い(ログが増える → エラーが増える → ログがさらに増える)ので、余裕をもって鳴らします。

定期的に上位10件を見る習慣

月1回、sudo ncdu -x / を眺めるだけでも、増え続けているものに気づけます。私はこれで、バックアッププラグインの世代が溜まっているのを2回発見しました。

それでも足りないときは、容量を増やす

ConoHa VPS を実際に3年使ったうえでの評価(料金の落とし穴や、まとめトクの中途解約の扱いを含む)はConoHa VPSの評判・料金にまとめています。増やす前に読んでおくと判断しやすいはずです。

ここまでやって——ログもキャッシュも掃除して、増え続ける設定も直して、それでも常時80%を超えている。その場合は、単純に容量が足りていません。ここで初めて増強を検討します。

増やす前に、このチェックリストを全部通したか

増強を検討する前の最終チェック
  1. sudo du -xh -d 1 / で犯人を特定し、消せるものは消した(原因①)
  2. df -i を見て、inode 枯渇ではないことを確認した(原因②)
  3. sudo lsof -nP +L1 で、削除済みなのに掴まれているファイルが無いことを確認した(原因③)
  4. journalctl --disk-usage と logrotate を見て、ログの上限を設定した(原因④)
  5. Docker のイメージ・snap の旧リビジョン・DB の binlog・バックアップの世代を整理した
  6. それでも使用率が下がらない

1〜5 をやらずに容量だけ増やすと、同じ速度で埋まって同じ場所に戻ってきます。私の経験上、初回の満杯はほぼ全部 1〜4 で解決します。増強が本当に必要なのは「データそのものが増えている」ケース(画像・動画・DB・バックアップ)だけです。

上位プラン/追加ディスクの実務

そのうえで容量を増やす場合、VPS には2つの手段があります。

ディスクだけ追加する(ConoHa VPS で容量を増やす唯一の手段)

ConoHa VPS には追加ディスク(追加SSD)があり、追加できるのは停止しているVPSのみです。基本のSSDに加えて追加できる上限が決まっています

プランを上げる

メモリと CPU コアが上がります。ただし 🔴 ConoHa VPS の場合、プランを上げても SSD 容量は増えません(1GB以上のプランはすべて同じ容量です)。スペック変更ができるのも停止しているVPSのみで、さらに 512MBプランと1GB以上のプランの間は変更自体ができません。つまり容量目的でのプランアップは効果がないので、容量が足りないなら追加ディスク一択になります

どちらもサーバーの停止が必要=ダウンタイムが発生するという点は共通です。「満杯で止まってから慌てて増やす」より、80% の通知で気づいて計画的に停止したほうが確実に楽です。追加ディスクは OS 側でのマウント作業も必要になるので、作業時間を確保できる時間帯にやってください。

なお、どのプランでどれだけの容量が必要かという目安は、この記事では扱いません。契約前の見積もりは別記事の担当です。

料金・プランの刻み・キャンペーンは改定が早いので、最新の条件は必ず公式サイトで確認してください。各社の横並びは個人向けVPS比較2026にまとめています。

【PR】以下は ConoHa VPS の広告リンクです。機能・料金の詳細は公式サイトでご確認ください。

次に読む記事

この記事は「ディスクが満杯になった今」を解決するトラブルシュート専用です。前後は別記事が担当しています。

復旧作業で溶かした時間が自分にとっていくらの損失だったかは、当サイトの適正時給・単価の逆算ツールで出せます。数字にすると、監視を仕込む気になります。

そしてVPSの利用料やプラン変更の費用は事業の経費として扱えます(勘定科目の考え方はサーバー代・レンタルサーバー代の勘定科目と経費計上が担当です)。経費が増えたときに手取りがどう動くかは、シミュレーターで試算できます。

まとめ

No space left on device の対処(保存版)
  1. 応急処置sudo journalctl --rotate--vacuum-size=200Msudo apt cleanrm -rf /var/log/* は絶対にやらない
  2. 原因①(使用量)df -hT でどのファイルシステムかを特定 → sudo du -xh -d 1 / で上から辿る。消す前に必ず find | head で目視
  3. 原因②(inode)df -iIUse% が 100% なら確定。sudo du -x --inodes -d 1 / で犯人を特定し、find -delete で条件付き削除。ext4 は後から inode を増やせない
  4. 原因③(削除済み保持)rm したのに df が動かないなら sudo lsof -nP +L1。プロセスの reload か truncate -s 0 /proc/PID/fd/N で解放
  5. 原因④(ログ肥大)journalctl --disk-usage と logrotate。journald のドロップインは 99-(後勝ち)、sshd の 00-(先勝ち)とは逆
  6. 空けたら systemctl --failed でサービスの復旧を確認し、80% でしきい値通知を仕込む
  7. 1〜5 を全部やって足りないときだけ、上位プラン・追加ディスクを検討する

ディスク満杯は、原因さえ当たれば復旧は数分です。時間を溶かすのは、df -h だけ見て「消したのに空かない」と当てずっぽうで触っている時間のほうです。df -hdf -ilsof +L1journalctl --disk-usage。この4コマンドを順に打つ、とだけ覚えておいてください。

そして、埋まってから慌てないために、しきい値通知だけは今日のうちに仕込んでおくことをおすすめします。私はこれを入れてから、深夜にサイトが落ちる回数がはっきり減りました。

【PR】以下は ConoHa VPS の広告リンクです。

ConoHa VPS の機能・料金を公式サイトで確認する
df -h に空きがあるのに No space left on device が出ます。なぜですか?

代表的な原因は2つです。1つは inode の枯渇で、df -i を実行して IUse% が 100% になっていないか確認してください。inode はファイル1個ごとに1つ消費されるため、小さいファイルが大量にあると容量が空いていても新規作成できません。もう1つは ext4 の予約ブロックで、既定でファイルシステムの5%が root 専用に確保されており、一般ユーザーはそこに書き込めません(sudo tune2fs -l /dev/vda1 | grep -i 'reserved block count' で確認できます)。番外として、inotify の監視上限に達した場合も同じ ENOSPC が返ることがあります(cat /proc/sys/fs/inotify/max_user_watches で上限を確認できます)。

ログを rm で消したのに空き容量が増えません。どうすればいいですか?

削除済みのファイルを、プロセスが開いたまま掴んでいる状態です。sudo lsof -nP +L1 を実行すると、リンク数が0のまま開かれているファイルが一覧で出ます(lsof が入っていない場合は sudo find /proc/[0-9]*/fd -ls 2>/dev/null | grep '(deleted)')。掴んでいるサービスを再起動するか、Nginx なら sudo nginx -s reopen でログを開き直せば解放されます。再起動できない場合は、lsof で分かった PID と FD 番号を使って sudo truncate -s 0 /proc/1024/fd/5 のように切り詰めます。ただしこの方法はログファイル以外には使わないでください(データが失われます)。次からは rm ではなく sudo truncate -s 0 /var/log/nginx/access.log を使えば、この問題自体が起きません。

journalctl --vacuum-size= を実行したのに使用量が減りません。

--vacuum-size=アーカイブ済みのジャーナルファイルだけを削除する仕様で、いま書き込み中のアクティブなファイルには手を付けません(journalctl の man に明記されています)。先に sudo journalctl --rotate を実行して現行ファイルをアーカイブに回してから vacuum すると、狙いどおり減ります。恒久的には /etc/systemd/journald.conf.d/99-size.confSystemMaxUse=500M などを書いて sudo systemctl restart systemd-journald してください。systemd のドロップインは「後に読まれたファイルが勝つ」ため、番号は 99- のように大きいものを付けます(sshd_config のドロップインとは逆なので注意)。反映は systemd-analyze cat-config systemd/journald.conf で確認できます。

容量が足りないので、プランを上げれば解決しますか?

🔴 ConoHa VPS の場合、プランアップは容量対策になりません。1GB以上のプランは SSD 容量がすべて同じなので、プランを上げてもメモリと CPU が増えるだけです。容量を増やす手段は追加ディスクになります。

そもそも、原因がログの肥大や inode 枯渇だった場合は、容量を増やしても同じ速度で埋まって同じ状態に戻ります。まず本文の4原因を全部確認してください。そのうえで追加ディスクを使う場合、追加できるのは「停止しているVPS」のみなのでダウンタイムが発生します。条件は公式サポートで最新をご確認ください。必要な容量の目安は別記事にまとめています。


出典

  • Ubuntu マニュアル journald.conf(5)(resolute。SystemMaxUse は既定でファイルシステムの10%・SystemKeepFree は15%、いずれも 4G が上限/ドロップインは *.conf.d/ から読まれる):https://manpages.ubuntu.com/manpages/resolute/en/man5/journald.conf.5.html (2026年8月25日確認)
  • Ubuntu マニュアル journalctl(1)(resolute。--disk-usage はアクティブ+アーカイブの合計/vacuum はアーカイブ済みジャーナルのみを対象とする):https://manpages.ubuntu.com/manpages/resolute/en/man1/journalctl.1.html (2026年8月25日確認)
  • Ubuntu マニュアル systemd-system.conf(5)(resolute。*.conf.d/ のドロップインは辞書順で読まれ、最後のファイルの値が優先される):https://manpages.ubuntu.com/manpages/resolute/en/man5/systemd-system.conf.5.html (2026年8月25日確認)
  • Ubuntu マニュアル logrotate(8)(resolute。-d は変更もステート更新もしないデバッグ実行/-f は強制ローテーション/設定ファイルは group-writable・world-writable であってはならない/copytruncate はコピーと切り詰めの間にログが失われる可能性/ステートファイルは /var/lib/logrotate/status):https://manpages.ubuntu.com/manpages/resolute/en/man8/logrotate.8.html (2026年8月25日確認)
  • Ubuntu マニュアル mke2fs(8)(resolute。予約ブロックの既定は5%/bytes-per-inode 比はファイルシステム作成後に変更できない):https://manpages.ubuntu.com/manpages/resolute/en/man8/mke2fs.8.html (2026年8月25日確認)
  • ConoHa VPS サポート「VPSのプランを変更する」(スペックを変更できるのは停止しているVPSのみ/512MBプランと1GB以上のプラン間は変更不可):https://support.conoha.jp/v/changeplan/ (2026年8月25日確認)
  • ConoHa VPS サポート「ディスク最大容量の変更」(ディスクの追加を行えるのは停止しているVPSのみ/基本のSSDに加えて追加できる上限あり):https://support.conoha.jp/v/changedisk/ (2026年8月25日確認)

※ソフトウェアの挙動はバージョンによって変わります。コマンドの結果は必ずご自身の環境で確認してください。コントロールパネルの画面構成・機能名・料金・プランの条件は変更されることがあるため、最新の情報は各社公式のサポートページでご確認ください。破壊的な操作(削除・切り詰め・パーティション変更)は、可能な限りバックアップまたはイメージ保存を取ってから実施することをおすすめします。

更新日:2026年8月25日

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

コスト最小で制作をしたいという意識強め(笑)

目次