logrotateとは、大きくなり続けるログファイルを「日付やサイズの区切りで別名に移し、古いものを圧縮・削除する」Linux標準のツールです。 設定は /etc/logrotate.d/ に1ファイル置くだけで、多くのディストリビューションでは1日1回、cron または systemd timer から自動で呼ばれます。

この記事では、ディレクティブの早見表と、そのまま貼れる設定テンプレート(nginx・Node.js・Java・Python・独自ログ)をまとめました。さらに、Ubuntu 24.04 の logrotate 3.21.0 を使って、-d(ドライラン)・-f(強制実行)・-s(状態ファイル指定)を実際に動かし、ログを書き続けるプロセスに対して copytruncate と create+postrotate を試したときに「何行欠けたか」を数えた実測結果を載せています。よく出るエラー(parent directory has insecure permissions など)も実際に再現して、直し方と一緒に示します。

logrotateディレクティブの早見表

設定ファイルの中で使う主なディレクティブ(命令)です。迷ったら「daily / rotate / missingok / notifempty / compress / delaycompress / create」の7つから始めると、ほとんどのログに対応できます。

ディレクティブ 書き方の例 意味 使いどころ
daily / weekly / monthly / yearly daily 日・週・月・年ごとにローテート 時間で区切る基本設定
hourly hourly 1時間ごと(logrotate自体を毎時実行する必要あり) 大量に出るログ
rotate rotate 14 残す世代数。超えた分は削除 保存期間=間隔×世代数
size size 100M サイズが超えたらローテート(時間指定より優先) サイズだけで区切りたい
maxsize maxsize 500M 時間が来ていなくても、このサイズを超えたらローテート dailyと併用で急増に備える
minsize minsize 1M 時間が来て、かつこのサイズ以上ならローテート 小さいログを細切れにしない
compress compress 古い世代をgzip圧縮 ディスク節約
delaycompress delaycompress 直前の世代(.1)だけ圧縮を次回に回す 書き込み中のプロセスがある時
missingok missingok ログが無くてもエラーにしない ほぼ常に付ける
notifempty notifempty 空のログはローテートしない 空ファイルの世代を増やさない
create create 0640 www-data adm 移動後に同名の新しい空ファイルを作る(権限・所有者) 標準の方式
copytruncate copytruncate コピーしてから元ファイルを0バイトに切り詰める ログを開き直せないアプリ
dateext dateext 世代番号ではなく日付を付ける(app.log-20261001) 日付で探したい
dateformat dateformat -%Y%m%d-%H%M%S dateextの書式。1日に複数回ならHMSを付ける hourly・size併用時
postrotate 〜 endscript 下のテンプレート参照 ローテート後に実行するシェル シグナルでログを開き直させる
prerotate 〜 endscript 同上 ローテート前に実行するシェル 事前処理
sharedscripts sharedscripts ワイルドカードで複数ログが対象でも、スクリプトを1回だけ実行 *.log 指定時
su su appuser appgroup 指定ユーザー・グループの権限でローテート 親ディレクトリがroot以外書き込み可の時
olddir olddir /var/log/app/archive 古い世代を別ディレクトリへ ログ置き場を整理
createolddir createolddir 0750 root root olddirが無ければ作る olddirとセット
maxage maxage 30 ローテート済みで指定日数より古いものを削除 世代数と日数の両方で管理
include include /etc/logrotate.d 別ファイル・ディレクトリを読み込む logrotate.confで使用

保存期間は「間隔 × rotate」で決まります。daily + rotate 14 なら約2週間、weekly + rotate 4 なら約1か月分が残ります。

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「logrotateの設定方法|コピペ用テンプレートとディレクティブ一覧」

logrotateをインストールして既定の設定を確認する

インストールとバージョン確認

Ubuntu / Debian では apt で入ります(多くの環境では最初から入っています)。

sudo apt-get install -y logrotate
logrotate --version

実行結果(Ubuntu 24.04.4 LTS):

logrotate 3.21.0
    ...
    Default compress command:   /bin/gzip
    Default state file path:    /var/lib/logrotate/status

「Default state file path」が状態ファイルの場所です。logrotate は「各ログを最後にいつローテートしたか」をここに記録し、次回の判定に使います。

設定ファイルの構成

ファイル 役割
/etc/logrotate.conf 全体の既定値。最後に include /etc/logrotate.d で個別設定を読む
/etc/logrotate.d/* ログごとの設定。自分で追加するのはここ
/var/lib/logrotate/status 状態ファイル(最終ローテート日時)

Ubuntu 24.04 の /etc/logrotate.conf(コメントと空行を除く):

weekly
su root adm
rotate 4
create
include /etc/logrotate.d

個別ファイルに書かなかった項目は、この既定値(weekly・rotate 4・create)が使われます。 個別設定で daily を書けば、そのログだけ daily になります。

設定ファイルの書き方(基本形)

/var/log/myapp/app.log {
    daily
    rotate 14
    missingok
    notifempty
    compress
    delaycompress
    create 0640 appuser appgroup
}

1行目に対象のパス(ワイルドカード・複数可)、{ } の中にディレクティブを1行1つ書きます。設定ファイルは root 所有・644 にします(緩い権限だと読み飛ばされます。後述)。

-d・-f・-sで動作確認する手順

本番のログで試す前に、作業用ディレクトリのダミーログで動きを確かめるのがおすすめです。ここでは /tmp/t89lr を作業場所として使いました。

準備:ダミーログと設定ファイル

mkdir -p /tmp/t89lr/logs && cd /tmp/t89lr
for i in $(seq 1 200); do echo "2026-10-01T10:00:00 INFO order id=$i created"; done > logs/app.log

cat > app.conf <<'EOF'
/tmp/t89lr/logs/app.log {
    daily
    rotate 3
    missingok
    notifempty
    compress
    delaycompress
    create 0640 root root
}
EOF

-s で状態ファイルを作業場所に指定すると、システムの /var/lib/logrotate/status を書き換えずに試せます。

-d(ドライラン):何も変更せずに判定だけ見る

logrotate -d -s /tmp/t89lr/status /tmp/t89lr/app.conf

実行結果:

warning: logrotate in debug mode does nothing except printing debug messages!  Consider using verbose mode (-v) instead if this is not what you want.

reading config file /tmp/t89lr/app.conf
Reading state from file: /tmp/t89lr/status
state file /tmp/t89lr/status does not exist
...
rotating pattern: /tmp/t89lr/logs/app.log  after 1 days (3 rotations)
empty log files are not rotated, old logs are removed
considering log /tmp/t89lr/logs/app.log
Creating new state
  Now: 2026-10-01 22:52
  Last rotated at 2026-10-01 22:00
  log does not need rotating (log has already been rotated)

ここがつまずきやすいポイントです。状態ファイルにまだ記録が無いログは、初回は「今ローテートした」扱いで記録だけされ、実際にはローテートされません。daily を入れた当日に何も起きないのはこのためです。続けて -v(詳細表示つきの通常実行)で実行すると、状態ファイルに "/tmp/t89lr/logs/app.log" 2026-10-1-22:0:0 と記録が作られ、翌日以降の実行から動き始めます。

-f(強制):時間を待たずにローテートする

-f は「時間やサイズの条件を無視して今すぐローテート」します。5回続けて実行し、ファイルの変化を ls -l で追いました。

for n in 1 2 3 4 5; do
  echo "== force $n =="
  echo "line from round $n" >> logs/app.log
  logrotate -f -s /tmp/t89lr/status /tmp/t89lr/app.conf
  ls -l logs | tail -n +2
done

実行結果(5回目は4回目と同じ並びのため省略):

== force 1 ==
-rw-r----- 1 root root    0 Oct  1 22:52 app.log
-rw-r--r-- 1 root root 9110 Oct  1 22:52 app.log.1
== force 2 ==
-rw-r----- 1 root root   0 Oct  1 22:52 app.log
-rw-r----- 1 root root  18 Oct  1 22:52 app.log.1
-rw-r--r-- 1 root root 575 Oct  1 22:52 app.log.2.gz
== force 3 ==
-rw-r----- 1 root root   0 Oct  1 22:52 app.log
-rw-r----- 1 root root  18 Oct  1 22:52 app.log.1
-rw-r----- 1 root root  38 Oct  1 22:52 app.log.2.gz
-rw-r--r-- 1 root root 575 Oct  1 22:52 app.log.3.gz
== force 4 ==
-rw-r----- 1 root root  0 Oct  1 22:52 app.log
-rw-r----- 1 root root 18 Oct  1 22:52 app.log.1
-rw-r----- 1 root root 38 Oct  1 22:52 app.log.2.gz
-rw-r----- 1 root root 38 Oct  1 22:52 app.log.3.gz

結果から読み取れること:

観察 理由
毎回 app.log が0バイトで新しく作られ、権限が -rw-r----- create 0640 root root
.1 は圧縮されず、.2 以降が .gz delaycompress(直前の世代だけ圧縮を1回遅らせる)
4回目以降、9110バイトの最初のログ(575バイトの.gz)が消えた rotate 3 で4世代目が削除された

-d と -f を組み合わせる:強制した場合の手順を確認

-d -f にすると「強制したら何をするか」を変更なしで表示できます。postrotate を含む設定で試すと、次のようにリネームの順番とスクリプトの中身まで出ます(抜粋)。本番に入れる前の最終チェックに便利です。

rotating pattern: /tmp/t89lr/dd/app.log  forced from command line (3 rotations)
...
renaming /tmp/t89lr/dd/app.log to /tmp/t89lr/dd/app.log.1
creating new /tmp/t89lr/dd/app.log mode = 0640 uid = 0 gid = 0
running postrotate script
running script with args /tmp/t89lr/dd/app.log /tmp/t89lr/dd/app.log.1: "
        echo reopen
"

size:サイズで区切る

size 1k を指定し、2048バイトのログと6バイトのログを置いて -v で実行しました。

considering log /tmp/t89lr/sz/big.log
  log needs rotating
considering log /tmp/t89lr/sz/small.log
  log does not need rotating (log size is below the 'size' threshold)

size を書くと時間間隔は見ずにサイズだけで判定します。「毎日、ただし大きくなったら途中でも」としたい場合は daily + maxsize を使います。なお、logrotate 自体が1日1回しか動かない環境では、サイズ判定も1日1回です。

やりたいこと 書き方
サイズだけで区切る size 100M
毎日。ただし500MBを超えたらその時点で daily + maxsize 500M(logrotateを毎時実行)
毎日。ただし1MB未満なら見送る daily + minsize 1M

copytruncateとcreate+postrotateの違いを実測

ローテートには大きく2つの方式があります。

方式 動き アプリ側に必要なこと
create(既定) 元ファイルを app.log.1 にリネーム → 新しい app.log を作成 ローテート後にログを開き直す(シグナル等で)
copytruncate 元ファイルを app.log.1 にコピー → 元ファイルを0バイトに切り詰め 何もしなくてよい

Linux では、プロセスはファイルを「名前」ではなく開いたときの実体(inode)で持ち続けます。create方式でリネームしただけだと、アプリは app.log.1 に書き続けます。 そこで postrotate でシグナルを送り、ログを開き直してもらいます。copytruncate は同じファイルを使い続けるので開き直し不要ですが、コピーと切り詰めの間に書かれた行は失われます。

検証用のアプリと集計スクリプト

連番の行を高速で書き続け、SIGHUP を受けたらログを開き直すダミーアプリです(Python 3.11)。

# writer.py: 連番の行を書き続けるダミーアプリ(SIGHUPでログを開き直す)
import os, signal, sys, time
path = sys.argv[1]
mode = sys.argv[2] if len(sys.argv) > 2 else "a"   # "a"=追記(O_APPEND) / "w"=書き込み位置を自分で持つ
f = open(path, mode, buffering=1)
def reopen(signum, frame):
    global f
    f.close()
    f = open(path, "a", buffering=1)
signal.signal(signal.SIGHUP, reopen)
with open(path + ".pid", "w") as p:
    p.write(str(os.getpid()))
i = 0
while True:
    i += 1
    f.write(f"seq={i:07d} order created\n")
    if i % 50 == 0:
        time.sleep(0.001)

ローテート済みのファイルと現行ファイルをすべて読み、連番の欠けとNULバイト(\0)を数えるスクリプトです。

# check.py: 連番の欠け・重複・NULバイトを数える
import glob, gzip, re, sys
base = sys.argv[1]
seqs, nul = [], 0
for fn in sorted(glob.glob(base + "*")):
    if fn.endswith(".pid"): continue
    data = gzip.open(fn).read() if fn.endswith(".gz") else open(fn, "rb").read()
    nul += data.count(b"\x00")
    seqs += [int(m) for m in re.findall(rb"seq=(\d{7})", data)]
s = sorted(set(seqs))
missing = s[-1] - s[0] + 1 - len(s)
print(f"files={len(glob.glob(base+'*'))-1} lines={len(seqs)} first={s[0]} last={s[-1]} missing={missing} dup={len(seqs)-len(s)} nul_bytes={nul}")

3つの設定を用意しました。ct.conf は下の create 行を copytruncate に替えて postrotate〜endscript を消したもの、nos.conf は postrotate〜endscript を消したものです(パスはそれぞれ ct/・nos/)。

# cr.conf(create + postrotate でSIGHUP)
/tmp/t89lr/cr/app.log {
    rotate 10
    create 0644 root root
    missingok
    notifempty
    postrotate
        kill -HUP "$(cat /tmp/t89lr/cr/app.log.pid)"
    endscript
}

それぞれ、アプリを起動して1秒ごとに3回 logrotate -f を実行しました。

for c in ct cr nos; do
  python3 writer.py /tmp/t89lr/$c/app.log a &
  sleep 1
  for k in 1 2 3; do logrotate -f -s /tmp/t89lr/st_$c /tmp/t89lr/$c.conf; sleep 1; done
  kill %1; wait
  echo "== $c =="; ls -l $c | tail -n +2; python3 check.py /tmp/t89lr/$c/app.log
done

実行結果(pidファイルの行は省略):

== ct ==
-rw-r--r-- 1 root root 1030900 Oct  1 22:52 app.log
-rw-r--r-- 1 root root 1020500 Oct  1 22:52 app.log.1
-rw-r--r-- 1 root root 1037400 Oct  1 22:52 app.log.2
-rw-r--r-- 1 root root 1071200 Oct  1 22:52 app.log.3
files=4 lines=160000 first=1 last=163600 missing=3600 dup=0 nul_bytes=0
== cr ==
-rw-r--r-- 1 root root  949000 Oct  1 22:52 app.log
-rw-r--r-- 1 root root  982800 Oct  1 22:52 app.log.1
-rw-r--r-- 1 root root 1127100 Oct  1 22:52 app.log.2
-rw-r--r-- 1 root root 1014000 Oct  1 22:52 app.log.3
files=4 lines=156650 first=1 last=156650 missing=0 dup=0 nul_bytes=0
== nos ==
-rw-r--r-- 1 root root       0 Oct  1 22:52 app.log
-rw-r--r-- 1 root root 3749200 Oct  1 22:52 app.log.1
files=2 lines=144200 first=1 last=144200 missing=0 dup=0 nul_bytes=0
方式 欠けた行 結果のファイル 読み取れること
copytruncate 3,600行(3回のローテートで) 世代ごとにきれいに分かれる 書き込みが多いほど、コピー〜切り詰めの間の行が失われる
create + postrotate(SIGHUP) 0行 世代ごとにきれいに分かれる 開き直しができるアプリなら最も確実
create のみ(シグナルなし) 0行 全行が app.log.1 に入り続け、app.log は0バイトのまま アプリは移動後のファイルに書き続ける。notifempty により2回目以降は何も起きない

この検証は1秒に数万行という極端な書き込み量なので、通常のアプリで数千行が欠けるわけではありません。ただ、copytruncate には原理上「欠ける時間帯」があることは数字で確認できます。

copytruncate+追記モードでないアプリの組み合わせ

アプリが追記モード(O_APPEND、シェルなら >>)ではなく、書き込み位置を自分で持つモード(Pythonの "w"、シェルの >)で開いていると、切り詰め後も前の位置から書き続けます。

python3 writer.py /tmp/t89lr/ctw/app.log w &
sleep 1; logrotate -f -s /tmp/t89lr/st_ctw /tmp/t89lr/ctw.conf; sleep 1; kill %1
ls -ls /tmp/t89lr/ctw | tail -n +2
python3 check.py /tmp/t89lr/ctw/app.log
head -c 40 /tmp/t89lr/ctw/app.log | od -c

実行結果:

 992 -rw-r--r-- 1 root root 2039700 Oct  1 22:52 app.log
1000 -rw-r--r-- 1 root root 1020500 Oct  1 22:52 app.log.1
   4 -rw-r--r-- 1 root root       4 Oct  1 22:52 app.log.pid
files=2 lines=78250 first=1 last=78450 missing=200 dup=0 nul_bytes=1025700
0000000  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0
*
0000040  \0  \0  \0  \0  \0  \0  \0  \0

切り詰めたはずの app.log が約2MBのまま、先頭に約1MBのNULバイトが入りました(ファイルに穴が空いた状態。ls -s の実使用ブロックは992KB)。less で開くと ^@ が並んで見えます。nohup java -jar app.jar > app.log のように > でリダイレクトしている場合は >> に変えると、copytruncate 後に正しく先頭から書かれます。

どちらを選ぶかの判断表

アプリの状態 おすすめ
シグナルや reload でログを開き直せる(nginx、Apache、rsyslog など) create + postrotate
Python の WatchedFileHandler を使っている create(シグナル不要。下で検証)
ログを開き直す仕組みがなく、再起動もしたくない copytruncate(わずかな欠けを許容)
アプリ自身がローテート機能を持つ(logback、log4j2、pm2-logrotate など) アプリ側に任せ、logrotate の対象から外す
systemd サービスで標準出力に出している ファイルにせず journald に任せる(logrotate不要)

PythonのWatchedFileHandlerならcreateだけで欠けない

Python 標準の logging.handlers.WatchedFileHandler は、書き込みのたびにファイルのinodeを確認し、変わっていたら自動で開き直します。postrotate なしの create 設定(nos.conf と同じ内容)で同じ検証をしました。

# wf_writer.py
import logging, logging.handlers, sys, time
h = logging.handlers.WatchedFileHandler(sys.argv[1])
log = logging.getLogger("app"); log.addHandler(h); log.setLevel(logging.INFO)
i = 0
while True:
    i += 1
    log.info("seq=%07d order created", i)
    if i % 50 == 0:
        time.sleep(0.001)

実行結果:

-rw-r--r-- 1 root root 429000 Oct  1 22:53 app.log
-rw-r--r-- 1 root root 521300 Oct  1 22:53 app.log.1
-rw-r--r-- 1 root root 587132 Oct  1 22:53 app.log.2
-rw-r--r-- 1 root root 516568 Oct  1 22:53 app.log.3
files=3 lines=79000 first=1 last=79000 missing=0 dup=0 nul_bytes=0

欠け0行で、世代ごとに分かれました。 Python アプリで logrotate を使うなら、FileHandler を WatchedFileHandler に替えるのがいちばん手軽です。

コピペ用テンプレート集

/etc/logrotate.d/ に、対象ごとに1ファイルで置きます。置いたら sudo logrotate -d /etc/logrotate.d/ファイル名 で文法を確認してください。

nginx(Ubuntu 24.04 の apt 版に同梱の設定)

/var/log/nginx/*.log {
    daily
    missingok
    rotate 14
    compress
    delaycompress
    notifempty
    create 0640 www-data adm
    sharedscripts
    prerotate
        if [ -d /etc/logrotate.d/httpd-prerotate ]; then \
            run-parts /etc/logrotate.d/httpd-prerotate; \
        fi \
    endscript
    postrotate
        invoke-rc.d nginx rotate >/dev/null 2>&1
    endscript
}

nginx は USR1 シグナルでログを開き直します。sharedscripts があるので、access.log と error.log の2つがあっても postrotate は1回だけ実行されます。自分で書く場合は postrotate を次のようにしても同じ意味です。

    postrotate
        [ -f /run/nginx.pid ] && kill -USR1 "$(cat /run/nginx.pid)"
    endscript

Node.js(node app.js >> app.log で動かしている場合)

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

Node.js の fs.createWriteStream には開き直しの仕組みが無いため copytruncate を使います。pm2 で動かしている場合は、create 方式にして postrotate で pm2 reloadLogs(pm2 がログを開き直すコマンド)を呼ぶか、pm2-logrotate モジュールに任せる方法もあります。

Java(標準出力をファイルにリダイレクトしている場合)

/var/log/myapp/stdout.log {
    daily
    rotate 30
    missingok
    notifempty
    compress
    delaycompress
    copytruncate
    maxsize 500M
}

起動コマンドは >>(追記)にします(> だと前述のNULバイトが入ります)。logback の RollingFileAppender や log4j2 の RollingFile で出しているログは、アプリ側でローテートされるので logrotate の対象に含めないでください。二重にローテートすると世代がずれます。

Python(WatchedFileHandler を使う場合)

/var/log/myapp/app.log {
    daily
    rotate 14
    missingok
    notifempty
    compress
    delaycompress
    create 0640 appuser appgroup
}

FileHandler のままの場合は、copytruncate を足すか、SIGHUP でハンドラーを開き直す処理を入れて postrotate で kill -HUP を送ります。

独自ログ(バッチの出力を日付付きで別ディレクトリに保存)

/var/log/batch/*.log {
    weekly
    rotate 12
    maxage 90
    missingok
    notifempty
    compress
    dateext
    dateformat -%Y%m%d
    olddir /var/log/batch/archive
    createolddir 0750 root adm
    su root adm
    create 0640 root adm
}

実際に olddir + createolddir + dateext + dateformat -%Y%m%d-%H%M%S + compress で試すと、次のようになりました。

od/archive:
-rw-r--r-- 1 root root 22 Oct  1 22:52 app.log-20261001-225251.gz

dateext の既定書式は -%Y%m%d(1日1つ)です。1日に2回以上ローテートする可能性がある場合(hourly、size、maxsize)は、%H%M%S まで入れると名前が重ならずに済みます。

cronとsystemd timer:logrotateは誰が動かしているか

logrotate は常駐しません。cron か systemd timer が1日1回起動しています。Ubuntu 24.04 では両方の仕組みが入っており、次のように振り分けられています。

/etc/cron.daily/logrotate の先頭には「/run/systemd/system があれば何もせず終了(systemd timer に任せる)」という分岐があり、/lib/systemd/system/logrotate.timer には OnCalendar=daily、AccuracySec=1h、Persistent=true が書かれています。

項目 cron(/etc/cron.daily) systemd timer(logrotate.timer)
使われる条件 systemd が動いていない環境(コンテナ等) systemd が動いている通常のサーバー
実行時刻 cron.daily の時刻(anacron 環境では起動後に実行) 毎日0時ごろ(AccuracySec=1h で最大1時間ずれる)
止まっていた間の分 anacron があれば後で実行 Persistent=true で起動後に実行
実行状況の確認 /var/log/syslog など systemctl list-timers logrotate.timer、journalctl -u logrotate
時刻・頻度の変更 crontab に独自行を追加 sudo systemctl edit logrotate.timer で OnCalendar= を上書き

上の2ファイルの中身は実際に確認しましたが、この検証用コンテナは systemd が PID 1 で動いていないため、systemctl list-timers などの timer 操作はこの環境では未実行です。

hourly や maxsize を効かせたい場合は、logrotate 自体を毎時動かします。

# sudo systemctl edit logrotate.timer で開いたファイルに書く
[Timer]
OnCalendar=
OnCalendar=hourly

OnCalendar= を一度空にしてから書くのは、既定の daily を消して置き換えるためです。cron で動かす場合は、/etc/cron.hourly/ に /usr/sbin/logrotate /etc/logrotate.conf を呼ぶスクリプトを置きます(cron の書式は「cronの書き方」の記事で詳しく扱っています)。

よくあるエラーと直し方(実際に再現)

parent directory has insecure permissions

ログのあるディレクトリが、root 以外のグループや全員から書き込める状態(例:777)のときに出ます。

error: skipping "/tmp/t89lr/ins/app.log" because parent directory has insecure permissions (It's world writable or writable by group which is not "root") Set "su" directive in config file to tell logrotate which user/group should be used for rotation.

直し方は2通りです。

方法 書き方
ディレクトリの権限を絞る chmod 755 /var/log/myapp
そのログの所有者で回すよう su を書く su appuser appgroup

su root root を追加したところ、exit=0 でローテートされ app.log.1 ができました。通常はログの所有者・グループを su に書くのが自然です。

Ignoring ... because it is writable by group or others

設定ファイルを 666 にして実行すると、error: Ignoring /tmp/t89lr/ins.conf because it is writable by group or others. と出てその設定は読み飛ばされ、終了コードは0でした。「なぜかローテートされない」原因になりやすいので、設定ファイルは chmod 644・chown root:root にします。

エラー一覧

症状・メッセージ 原因 対処
log does not need rotating (log has already been rotated) が初回に出る 状態ファイルに記録が無いログは初回は記録のみ 翌日まで待つか -f で確認
parent directory has insecure permissions ディレクトリが root 以外から書き込み可 chmod 755 または su
Ignoring ... because it is writable by group or others 設定ファイルが 664/666 など chmod 644
duplicate log entry 同じパスを複数設定で指定 片方を削除・移動
error verifying olddir path olddir が無い createolddir
ローテート後も古いファイルにだけ書かれる create 方式でアプリが開き直していない postrotate でシグナル、または copytruncate
切り詰め後もサイズが減らずNULが入る > リダイレクトで copytruncate >> に変更
ローテートしたのにディスクが空かない 削除済みファイルをプロセスが開いたまま 開き直させる(lsof +L1 で確認)

よくある質問

Q. 設定を変えたら logrotate の再起動は必要ですか? A. 不要です。logrotate は常駐せず、起動のたびに設定を読み直します。次回の定期実行から反映されます。すぐ試したい場合は sudo logrotate -d /etc/logrotate.d/対象 で確認してから sudo logrotate -f /etc/logrotate.d/対象 を実行します。

Q. copytruncate を使うとログは必ず欠けますか? A. コピーと切り詰めの間に書かれた行だけが対象なので、書き込みが少ないアプリでは欠けないことも多いです。この記事の検証では1秒に数万行書く条件で3回のローテートで3,600行欠けました。欠けを避けたい場合は、create + postrotate(シグナルで開き直し)を選びます。

Q. daily と size を両方書くとどうなりますか? A. logrotate 3.21 では、size は時間間隔とは別の条件で、後に書いたほうが有効になります。「毎日、ただし大きくなったら途中でも」は daily + maxsize、「毎日、ただし小さければ見送り」は daily + minsize を使うと意図どおりになります。

まとめ

  • logrotate は /etc/logrotate.d/ に1ファイル置くだけで、cron または systemd timer から1日1回動く
  • 基本は daily / rotate / missingok / notifempty / compress / delaycompress / create の7つ
  • -d でドライラン、-f で強制、-s で状態ファイルを分けて安全に試せる。初回は記録のみでローテートされない
  • create + postrotate(シグナル)は欠け0行、copytruncate は検証で3,600行欠けた。開き直せるアプリは create を選ぶ
  • copytruncate を使うなら、アプリは >>(追記モード)で開く。> だとNULバイトが入る
  • Python は WatchedFileHandler にすれば create だけで欠けなく分かれる
  • insecure permissions は su か chmod 755、設定ファイルは root 所有・644

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「logrotateの設定方法|コピペ用テンプレートとディレクティブ一覧」

株式会社RJCでは、ログ管理のような運用の基礎を仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?