Linuxサーバーを運用・保守する現場において、運用担当者やインフラエンジニアが最も警戒すべき障害の一つが 「ログファイルの肥大化によるディスク容量100%枯渇(Disk Full・サーバー停止)」 です。特に年末年始や大型連休など、エンジニアがオフィスを離れて無人稼働する期間にアクセスが集中したりバッチがエラーログを吐き続けたりすると、深夜に監視アラートが鳴り響き緊急対応を余儀なくされます。
このログ肥大化インシデントを未然に防ぎ、自動で健全なディスク容量を維持するための必須ツールが logrotate(ログローテート) です。logrotate を適切に設定しておけば、ログを日次や週次で新しいファイルに切り替え、過去のログを自動で gzip 圧縮し、保存期間を過ぎた古い世代を安全に自動消去してくれます。
しかし、いざ /etc/logrotate.d/ に設定ファイルを書こうとすると、 「なぜかログが圧縮されない」「書き込み中のログが欠損した」「パーミッションエラーで設定が丸ごと無視されていた」 といった現場ならではの落とし穴に直面しがちです。特にログ圧縮時の delaycompress の欠如や、権限の不整合はディスク溢れの直接原因になります。
本記事では、Webサーバー(Nginx / Apache)や独自アプリケーション(Python, Node.js, Go等)向けに 「コピペでそのまま動く推奨設定テンプレート」 をはじめ、主要ディレクティブの機能対比表、なぜ delaycompress を指定しないとログが壊れるのかの技術的理由、設定変更後に必須となるテスト実行コマンド(-d / -f)、そして年末年始などの長期休暇を安全に乗り切るディスク枯渇防止テクニックまで、実務目線で徹底解説します。
【コピペで動く】logrotate設定ファイルのテンプレート早見表
まずは、実務の現場でそのまま /etc/logrotate.d/ 配下に配置して即座に機能する推奨標準テンプレートと、主要ディレクティブの早見表を提示します。環境に合わせてファイルパスや保持世代数を微調整するだけで安全に運用を開始できます。
Webサーバー・独自アプリログ向けの推奨標準設定テンプレート
サーバー運用で最も頻出する「Webサーバー(シグナル再オープン方式)」と「独自アプリケーション(copytruncate方式)」の2大推奨設定です。目的に応じて選択してください。
パターン1:Webサーバー向け(Nginx / Apache等のシグナル方式)
Nginx や Apache など、シグナル送信によってサービス無停止でログファイルを再オープン(Reopen)できるミドルウェア向けの標準設定です。ログの取りこぼしが一切発生しません。
# /etc/logrotate.d/nginx
/var/log/nginx/*.log {
# 毎日ローテーションを実行
daily
# ログファイルが存在しなくてもエラーを出さずスキップ
missingok
# 過去14世代(14日分)を保持し、超えた古いログは自動削除
rotate 14
# 過去ログをgzipで圧縮してディスク容量を節約
compress
# 直近の過去ログ(.1)の圧縮を次回まで遅延(書き込み競合・破損防止)
delaycompress
# ログファイルが空(0バイト)の場合はローテーションしない
notifempty
# ローテーション後に空の新規ファイルを権限0640、所有者www-data:admで作成
create 0640 www-data adm
# 複数ログが存在してもpostrotateスクリプトを1回だけ実行
sharedscripts
# 過去ログに連番ではなく日付サフィックス(YYYYMMDD)を付与
dateext
dateformat -%Y%m%d
# ローテーション完了後にNginxへログ再オープンシグナル(USR1)を送信
postrotate
if [ -f /var/run/nginx.pid ]; then
kill -USR1 $(cat /var/run/nginx.pid)
elif [ -f /run/nginx.pid ]; then
kill -USR1 $(cat /run/nginx.pid)
fi
endscript
}
パターン2:独自アプリケーション向け(Python / Node.js / Go / Docker等)
Python(FastAPI / Django)、Node.js、Go、Rails、Dockerコンテナなど、シグナル受信機構を持たない独自アプリケーション向けの万能設定です。copytruncate を採用することで、プロセス再起動なしで安全に切り替えます。
# /etc/logrotate.d/myapp
/var/log/myapp/*.log {
# 毎日ローテーションを実行
daily
# 30世代(約1ヶ月分)保持
rotate 30
missingok
notifempty
compress
delaycompress
# 現行ログを別名コピーした後に中身を切り詰めてサイズ0にする(再起動不要)
copytruncate
# 突発的なアクセス増で500MBを超えたら日次を待たずにローテーション
maxsize 500M
dateext
dateformat -%Y%m%d
# 非rootユーザー権限で動作しているディレクトリのパーミッション警告を回避
su appuser appgroup
}
設定ディレクティブ一覧(daily, rotate, compress, missingok等)の機能対比表
logrotate の設定ファイルで使用する主要ディレクティブの役割、デフォルト動作、および実務における推奨指定値を一覧表でまとめました。
| ディレクティブ | デフォルト値 | 実務の推奨値 | 役割・設定のポイント |
|---|---|---|---|
| daily / weekly / monthly | なし(必須指定) | daily |
ローテーションを実行する時間周期。日次ログ集計や容量管理のしやすさから daily が実務の標準。 |
| rotate <count> | 0(残さない) | rotate 14 〜 30 |
保持する過去ログの世代数。上限を超えた最も古いログファイルは自動的に削除される。 |
| compress | nocompress |
compress |
ローテーション済みの過去ログを gzip で圧縮する。テキストログの容量を約70〜90%削減可能。 |
| delaycompress | なし | delaycompress |
compress と併用し、直前の世代(.1)の圧縮を次回まで1回見送る。書き込み競合によるログ破損を防ぐ必須設定。 |
| missingok | nomissingok |
missingok |
対象ログファイルが存在しない場合でもエラーを出さずにスキップ。予期せぬサービス未起動時の失敗を防ぐ。 |
| notifempty | ifempty |
notifempty |
ログファイルが空(0バイト)の場合はローテーションを行わない。無駄な空圧縮ファイルの量産を防止。 |
| copytruncate | なし | 用途に応じて指定 | 現行ファイルをコピー後に元ファイルを切り詰めて0バイト化。プロセス再起動やシグナル対応が困難なアプリに最適。 |
| create <mode> <user> <group> | 元ファイルの属性 | create 0640 www-data adm |
ローテーション直後に新規作成する空ログファイルのパーミッションと所有者・グループを明示指定。 |
| dateext | なし(.1, .2の連番) | dateext |
過去ログの末尾に連番ではなく日付(YYYYMMDD)を付与。S3アーカイブやトラブル調査が格段に容易になる。 |
| maxsize <size> | なし | maxsize 500M など |
周期(daily等)に達していなくても、指定サイズを超えた時点でローテーションを実行。ログ急増時の防波堤。 |
| sharedscripts | なし(ログ毎実行) | sharedscripts |
ワイルドカードで複数ログが対象となった場合でも、postrotate スクリプトを全体で1回だけ実行する。 |
| su <user> <group> | なし(root実行) | 非root所有時に指定 | 指定したユーザー・グループ権限でローテーション処理を実行。パーミッション警告(insecure permissions)を解消。 |
logrotateの主要設定ディレクティブと圧縮設定
ここからは、安定したログローテーションと確実なディスク容量保護を実現するために、各ディレクティブの内部挙動と設計上の急所を深掘りして解説します。
世代管理とローテーション契機(rotate / daily / weekly / size)
ログのローテーション契機は、「時間(期間)」または「ファイルサイズ」によって決定されます。
- 時間ベースの契機 :
daily(日次)、weekly(週次)、monthly(月次)、yearly(年次)が指定可能です。実務では調査の単位が「特定の日時」になりやすいため、dailyで日ごとにファイルを区切る運用が最も扱いやすくトラブル調査の工数を削減できます。 - 世代数の管理(
rotate) :rotate 14と指定した場合、14世代分の過去ログが保持され、15世代目が発生したタイミングで最古のログが自動消去されます。rotate 0を設定すると、過去ログを保持せずローテーション直後に即座に消去されます。 - サイズベースの契機(
size) :size 100Mのように指定すると、時間周期に関係なくファイルサイズが100MBを超えた時点でローテーションされます。ただし、sizeのみを指定すると日次での日付管理が崩れるため、後述するmaxsizeとdailyの併用が推奨されます。
ログ圧縮設定(compress / delaycompress)の正しい組み合わせ
ログローテーションにおける最重要設定が compress と delaycompress のペアリングです。なぜ compress 単体では危険で、 必ず delaycompress を併用しなければならないのか 、その技術的背景を理解しておく必要があります。
Linuxのファイル記述子(inode)とログ破損のメカニズム
Linux では、プロセスがログファイルを開くと、カーネル内で ファイル記述子(File Descriptor) が生成され、ファイルの実体である inode を直接参照し続けます。プロセスはファイル名を見ているのではなく inode 番号を見て書き込みを行っています。
- ローテーションが実行されると、まず現行ログ(
app.log)が別名(app.log.1)にリネームされます。この時点では inode 番号は変わらないため、プロセスはリネームされたapp.log.1にログを書き込み続けています。 - 次に
createで新しい空のapp.logが作成され、postrotateでプロセスに再オープンシグナル(USR1やHUP)が送られます。 - プロセスがシグナルを受け取って新しい
app.logを開き直すまでには、ごくわずかなタイムラグ(数ミリ秒〜数秒)が存在します。
もし delaycompress を指定していない場合、logrotate はリネーム直後の app.log.1 に対して プロセスがまだ書き込みを行っている最中に gzip 圧縮処理を開始してしまいます 。その結果、以下の重大トラブルが発生します:
- gzip が圧縮処理中のファイルにプロセスが追記するため、 gzipアーカイブが壊れて解凍不能になる 。
- プロセス側で
write error (Bad file descriptor)が発生し、 書き込み途中のアクセスログやトランザクションログが永久に欠損する 。
ここで delaycompress を指定しておくと、直近にリネームされた app.log.1 は平文のまま残され、圧縮処理は 次回のローテーション時(24時間後)まで延期 されます。次回ローテーション時にはプロセスは完全に新しい app.log へ切り替わっているため、100%安全に gzip 圧縮が行われます。 「compress を書くときは必ずセットで delaycompress も書く」 と覚えておきましょう。
権限指定と新規ファイル生成(create / su ディレクティブ)
ログローテーション後、アプリケーションが引き続きログを出力できるように新規ファイルの権限を正しく設計することが不可欠です。
# 基本書式: create <パーミッション> <所有ユーザー> <所有グループ>
create 0640 www-data adm
createディレクティブにより、ローテーション完了直後に新しい空ファイルが指定したパーミッション・所有者で生成されます。権限が厳しすぎるとアプリケーションがログファイルを開けず、緩すぎるとセキュリティリスク(他ユーザーからのログ覗き見・改ざん)につながります。su <user> <group>は、logrotate 自体を root 権限ではなく指定ユーザー・グループの権限で動作させるディレクティブです。ホームディレクトリ配下など非 root 所有のディレクトリにログを出力している場合、logrotate はセキュリティ保護のため動作を拒否します。この場合は必ずsuディレクティブで対象ユーザーを指定してください。
サービス再読み込みスクリプト(postrotate / sharedscripts)の記述法
シグナル方式を採用する場合、ログファイルを切り替えた後にミドルウェアへ「古いファイルを閉じて新しいファイルを開け」と通知しなければなりません。これが postrotate ... endscript です。
postrotate
# PIDファイルの存在を確認してから安全にシグナルを送信
if [ -f /run/nginx.pid ]; then
kill -USR1 $(cat /run/nginx.pid)
fi
endscript
ここで極めて重要なのが sharedscripts です。例えば /var/log/nginx/*.log と指定した場合、access.log や error.log、バーチャルホストごとのログなど複数ファイルが対象になります。sharedscripts を記述しないと、 対象ログファイル1つごとに postrotate スクリプトが実行され、Nginx に何度も連続でシグナルが送信されてしまいます 。複数ファイルをワイルドカードで指定する場合は、必ず sharedscripts を併記してください。
設定変更後の安全確認!テスト実行とデバッグ手順
logrotate の設定ファイルを作成・編集した後は、 「次の定期実行まで待つ」のは厳禁 です。設定ミスがあれば無人稼働中にローテーションが停止し、ディスク溢れに直結します。必ずその場でコマンドを実行し、動作を検証してください。
設定の妥当性をシミュレーション確認するドライラン(logrotate -d)
設定ファイルの構文エラー(タイポ、括弧の閉じ忘れ等)や、対象ログファイルが意図通りマッチしているかを安全に確認するコマンドが ドライラン(デバッグモード:-d) です。
# 特定の設定ファイルを指定してドライランを実行
sudo logrotate -d /etc/logrotate.d/myapp
-d(--debug)オプションを付与すると、 実際のファイル名変更・圧縮・削除・スクリプト実行は一切行われません 。内部で行われる判定処理が標準出力へ詳細に出力されます。
| デバッグ出力メッセージ例 | 意味・確認ポイント |
|---|---|
reading config file /etc/logrotate.d/myapp |
設定ファイルが正常に読み込まれたことを示します。構文エラーがある場合はここでエラー行が表示されます。 |
considering log /var/log/myapp/app.log |
指定したワイルドカードに対象ログファイルが正しくマッチしたことを示します。 |
log does not need rotating (log has been rotated at ...) |
前回のローテーションから十分な時間(1日等)が経過していないため、現時点ではローテーション不要と正しく判定された状態です。 |
rotating log /var/log/myapp/app.log, log->rotateCount is 30 |
ローテーション条件を満たしており、30世代管理でローテーションが実行される計画であることを示します。 |
ドライランを実行してエラーメッセージ(error: ...)が1行も出力されなければ、設定ファイルの構文は100%正常です。
強制ローテーションによる即時動作確認(logrotate -f)
ドライランで構文を確認したら、次に実際にファイルを切り替えて動作を検証します。通常は前回のローテーションから24時間経過していないとスキップされますが、 強制実行フラグ(-f / --force) と 詳細出力フラグ(-v / --verbose) を組み合わせることで、今すぐテスト実行できます。
# 強制ローテーションを詳細ログ付きで実行
sudo logrotate -vf /etc/logrotate.d/myapp
実行後、実際にログディレクトリを確認して以下の3点を検証します:
- 新しい
app.logが生成され、設定通りのパーミッション・所有者(例:0640 www-data:adm)になっているか。 - 過去ログ(
app.log.1またはapp.log-YYYYMMDD)が作成されているか。 - アプリケーションからテストアクセスを送り、 新しい
app.logにリアルタイムでログが書き込まれ続けているか (tail -f /var/log/myapp/app.logで確認)。
実行状態と履歴の確認(/var/lib/logrotate/status の見方)
logrotate は「どのログファイルを、いつ最後にローテーションしたか」をステータスファイルに記録しています。Debian/Ubuntu では /var/lib/logrotate/status、RHEL/CentOS では /var/lib/logrotate.status に配置されます。
# ステータスファイルの最新記録を確認
sudo cat /var/lib/logrotate/status | grep "myapp"
# 出力例:
# "/var/log/myapp/app.log" YYYY-MM-DD-HH:MM:SS
ステータスファイルには、フルパスと最終ローテーション日時が記録されています。daily 指定の場合、この記録日時と同じ日の中では再実行がスキップされる仕組みです。テスト実行(-f)を行うとこのステータス日時は現在日時に更新されます。
長期休暇・年末年始に備えるログ肥大化・ディスク枯渇防止策
年末年始の約1週間から10日間にわたるエンジニア不在期間中、最も恐ろしいのは 「突発的なアクセス急増」や「特定のエラーによるログ連続出力(ログフラッド)」 です。日次ローテーションだけでは防ぎきれないディスク枯渇を確実に阻止する3大防衛策を講じておきましょう。
なお、連休前のログ総点検やディスク容量監視の自動化については、長期休暇前のサーバー停止を防ぐログ総点検記事をご覧ください。
アクセス急増時にも安心な maxsize / minsize の併用テクニック
通常時は daily で1日1回深夜にローテーションするのが理想ですが、無人稼働中に突如スパムアクセスやAPIループが発生すると、数時間で数十GBのログが吐き出され、次の深夜ローテーションを待たずにディスクがパンクします。これを防ぐのが maxsize です。
/var/log/myapp/*.log {
daily
# 通常は日次で回すが、万が一500MBを超えたらその場で即座にローテーション
maxsize 500M
rotate 14
compress
delaycompress
missingok
notifempty
copytruncate
}
daily と maxsize 500M を併用すると、ログサイズが500MB未満の間は通常通り1日1回ローテーションされますが、500MBを超過した場合は 定期実行のタイミングで24時間を待たずに即時ローテーション されます。肥大化したログが早期に .gz 圧縮されるため、ディスク容量の急激な枯渇を確実に防ぐことができます。
cron と systemd-timer(logrotate.timer)の実行スケジュール確認
logrotate 自体は常駐デーモンではなく、外部のスケジューラーによって定期実行されます。自環境が「どのスケジューラーで、何時に実行されているか」を把握していないと、想定外のタイミングでディスクがいっぱいになります。
近代的なLinux(Ubuntu, Debian, RHEL, AlmaLinux等):systemd-timer
近年の主要ディストリビューションでは、systemd のタイマー機能によって logrotate が定期実行されています。詳細な動作仕様やサービス管理は systemctlコマンド解説記事 を参照してください。
# logrotate タイマーの稼働状態と次回実行時刻を確認
systemctl status logrotate.timer
# タイマー一覧で次回の実行予定時刻をチェック
systemctl list-timers logrotate.timer
# 出力例:
# NEXT LEFT LAST PASSED UNIT ACTIVATES
# 日付 時刻 JST 7h left 日付 時刻 JST 16h ago logrotate.timer logrotate.service
従来のLinux環境:cron.daily
従来の環境では、/etc/cron.daily/logrotate に配置されたスクリプトが cron または anacron 経由で実行されます。crontab の詳しい時間指定や書式ルールは cron設定完全ガイド で解説しています。
# cron.daily 配下に logrotate スクリプトが存在するか確認
ls -l /etc/cron.daily/logrotate
ディスク容量監視(df / du コマンド)との連携
長期休暇に入る前に、必ずサーバー全体の空き容量と、現在どのログがディスクを消費しているかを点検する習慣をつけてください。
# 1. ファイルシステム全体の空き容量を確認(使用率が80%以上のパーティションを警戒)
df -h
# 2. /var/log 配下で容量を食っている上位10ディレクトリ・ファイルを特定
sudo du -sh /var/log/* 2>/dev/null | sort -rh | head -n 10
ディスク全体の空き領域を確認する dfコマンド と、特定ディレクトリ内の肥大化ファイルをあぶり出す duコマンド を組み合わせることで、どのサービスがログを圧迫しているかを瞬時に特定できます。使用率がすでに80%を超えている場合は、古い世代ログの整理や rotate 数値の見直しを行ってください。
よくあるトラブルと設定ミスの落とし穴
インフラ運用現場で実際に多発している logrotate のトラブル事例と、その即効性ある対処法をまとめました。
postrotateのkill -HUP失敗でログ書き込みが止まる問題と対処法
Webサーバーや各種デーモンの設定で最も多いのが、postrotate 内に記述した kill -HUP や kill -USR1 が失敗し、 以降一切のログが書き込まれなくなる問題 です。
| 失敗原因 | 現象と安全な解決手順 |
|---|---|
| PIDファイルのパス誤り | /var/run/nginx.pid にファイルが存在せず cat が失敗。kill に引数が渡らず終了ステータス1でエラー終了する。OSのバージョン差(/run と /var/run)を吸収するため、両方の存在をチェックする if [ -f ... ] 構文を書く。 |
| プロセス停止中のローテーション | メンテナンス等でWebサーバーが停止している間に logrotate が走ると、PIDファイルが存在せず kill が失敗してエラーになる。存在確認を必ず挟むことで回避可能。 |
| SELinuxによるシグナル拒否 | RHEL系で SELinux が有効な場合、logrotate ドメインから他プロセスへのシグナル送信がブロックされることがある。専用の reload コマンド(systemctl reload nginx 等)を使う。 |
「error: skipping because insecure permissions」パーミッション警告の解決手順
「設定ファイルを作成したのに、翌日になってもローテーションされない」という場合、システムログ(/var/log/syslog や journalctl -u logrotate)を確認すると、以下の警告が出ているケースが大半です。
error: skipping "/etc/logrotate.d/myapp" because parent directory has insecure permissions (It's world writable or writable by group which is not "root")
logrotate には厳格なセキュリティポリシーがあり、 設定ファイル自体や親ディレクトリのパーミッションが不正(他ユーザーに書き込み権限がある、所有者が root ではない)な場合、悪意あるコードの実行を防ぐため設定を丸ごと無視 します。
# 設定ファイル自体の権限を 0644、所有者を root:root に修正
sudo chmod 0644 /etc/logrotate.d/myapp
sudo chown root:root /etc/logrotate.d/myapp
# ログ出力先ディレクトリがアプリユーザー所有の場合は、設定ファイル内に su を明記
# 例: /etc/logrotate.d/myapp の先頭に以下を追加
# su appuser appgroup
設定ファイルのパーミッションを 0644、所有者を root:root に正しく修正し、アプリ専用ディレクトリの場合は su ディレクティブを併用することで、この警告は確実に解消されます。
まとめ
Linuxサーバーにおける logrotate 設定は、インフラの安定稼働とディスク枯渇インシデント防止を支える生命線です。本記事の要点を振り返ります。
- 標準テンプレートの活用 :Nginx などのWebサーバーは
create + postrotateによるシグナル方式、独自アプリケーションはcopytruncate方式を使い分ける。 - delaycompress の必須性 :現在書き込みを行っているプロセスのログ破損や欠損を防ぐため、
compress指定時は必ずdelaycompressを併用する。 - テスト実行の徹底 :設定変更後は放置せず、必ず
logrotate -dで構文をドライラン検証し、logrotate -vfで実際のファイル切り替えと追記を確認する。 - 無人稼働・長期休暇への備え :突発的なアクセス増に備えて
maxsizeを併用し、事前にdf/duコマンドでディスク使用状況を総点検しておく。
適切なログローテーションを設定し、年末年始や連休中もアラートに怯えることのない堅牢なシステム基盤を構築しましょう。

コメント