Logwatchのメールが「届いたり届かなかったり」する原因に追った(Proxmox/journald環境)

Proxmoxノードから毎朝届くはずのLogwatch通知メールが、ある日だけ届かなかった。配送エラーで起きがちなSPF/DKIM/DMARCはすべてPASS状態。そして、PostfixのstatusもActive表示で問題無し。にもかかわらず特定の1日分だけ抜けるという、地味だが気持ち悪い事象である。

原因にたどり着くまでの調査過程と、最終的な対策をまとめておく。

事象

Gmailの受信トレイを見ると、管理している他のサーバーからは同じ日付でLogwatchメールが届いているのに、Proxmoxからの通知だけが1日分抜けている。翌日になると遅れて(前日分の内容が)届く、という挙動だった。

最初はGmailのスレッド統合や Precedence: bulk ヘッダーによる自動分類を疑ったが、それらでは「特定の1日だけ抜ける」説明がつかない。

下表内容は本投稿内容を総括したもので、それぞれの環境でどのような事情なのか整理したもの。

項目 内容
Logwatch空送信 標準オプションなし。空レポートはメール生成自体をスキップする設計。--output mail では空でも送信されるが本文はヘッダーのみになる
Postfix + DKIM ドメインでDKIM署名済み(s=default)。SPF/DMARC も PASS。Gmailへの直接送信で認証が通っている
Gmailリレー経由 Gmail SMTPリレー(smtp.gmail.com)経由の場合、送信元ドメインの DKIMではなくGmailのDKIMが付与される。独自ドメインの DKIMは無効化されるケースがある
サーバ直送信 + DKIM 今回の構成(Postfix 直送信 + galaxies.jp DKIM)は Gmailの受信側で DKIM=PASS・SPF=PASS・DMARC=PASSとなり、スパム判定されにくい
スパム判定リスク 本文が極端に短い場合(今回の size=529 のケース)はスパムフィルタに引っかかるリスクがある。ただし今回は判定ではなくGmailのスレッド統合が原因で「見えなかった」だけで受信自体はされていた

切り分けしていくと意外な原因

cronは実行されているか

ProxmoxはDebianベースなので、cron.daily からLogwatchが起動する。

grep -r logwatch /etc/cron*
# /etc/cron.daily/00logwatch:/usr/sbin/logwatch --output mail

実行ログを確認。

journalctl --since "2026-06-13 06:25:00" --until "2026-06-13 06:26:00" -u cron

cron.daily は問題なく起動していた。anacronは未インストールで、test -x /usr/sbin/anacron || { ... } の条件分岐で run-parts がスキップされる線も否定できた。

実際のところメールは送信されたのか

Postfixのログを確認すると、その日の該当時刻にメール送信の痕跡がまったくないことがわかった。

journalctl --since "2026-06-13 06:25:00" --until "2026-06-13 06:30:00" | grep -i "postfix\|smtp\|logwatch"
# (出力なし)

Postfixデーモンはもちろん24時間無停止であり、前日分・翌日分は正常に送信されている。つまり「Logwatchは起動したが、メールをPostfixに渡さなかった」ということになる。

Logwatchは何を出力したのか

ここが決定的だった。当該日分のレポートを手動で再生成してみる。

/usr/sbin/logwatch --output stdout --range "2026-Jun-12" 2>&1 | wc -l
# 0

0行。前後の日付は正常に出力される。

/usr/sbin/logwatch --output stdout --range "2026-Jun-11" 2>&1 | wc -l
# 69
/usr/sbin/logwatch --output stdout --range "2026-Jun-13" 2>&1 | wc -l
# 71

Logwatchは レポートが空のときメールを送信しない。これが「届かなかった」直接の原因である。

問題は、なぜその日だけ何故空レポートになったのか…

なぜ空レポートになったのか

journaldからログは取れている

該当日のログ自体はもちろんjournaldに存在する。

journalctl --since "2026-06-12 00:00:00" --until "2026-06-12 23:59:59" | wc -l
# 1604

1604行ある。ログが無いわけではないと分かる。

Logwatchのjournald連携を追う

Proxmoxはrsyslogではなくjournaldを使うため、Logwatchは scripts/shared/journalctl というPerlラッパー経由でログを読む。このスクリプトは環境変数 LOGWATCH_DATE_RANGE を見て journalctl--since / --until が条件付けだ。

} elsif ( $range eq 'yesterday' ) {
    push @range, '--since', 'yesterday', '--until', 'today';
}

cron.daily からは --range 指定なしで呼ばれるため、標準の yesterday 指定が使われ、実体としては次のコマンドとパラメータが走っている。

journalctl --since yesterday --until today

試しにこれを手動で実行すると正常にログが返る。--range yesterday でも正常。つまり通常は問題なく動く

journalのローテーションがタイミング悪く詰まっていた

journalファイルのタイムスタンプを確認したところ、見逃せない事実が出てきた。

ls -la /var/log/journal/<machine-id>/
  • アクティブな system.journal の作成時刻が当日の 04:05
  • アーカイブされたjournalの最終エントリも 04:05

一方で標準設定のLogwatchが走るのは 06:25

つまり当該日の朝、Logwatchが「前日分(yesterday)」を読もうとした時点で、対象のログはすべて直前04:05のローテーションでアーカイブ側に移っていた。このローテーション直後の一時的な不整合により、journalctl --since yesterday --until today がその瞬間だけ前日分を返さなかった、というのが最も合理的な説明になる(事後に同じコマンドを叩くと正常に返るため、再現・確定は難しい)。

対策

Proxmoxではjournaldをrsyslogに置き換える選択肢を取りたくない。そこで「空レポートのときは黙ってスキップせず、警告メールを飛ばす」方針にした。これなら次に同じ事象が起きても確実に検知できる。

/etc/cron.daily/00logwatch を以下に差し替えた。

#!/bin/bash
#Check if removed-but-not-purged
test -x /usr/share/logwatch/scripts/logwatch.pl || exit 0
#execute
OUTPUT=$(/usr/sbin/logwatch --output stdout)
if [ -z "$OUTPUT" ]; then
    echo "WARNING: Logwatch produced empty output on $(hostname) - $(date)" | \
        mail -s "Logwatch EMPTY REPORT for $(hostname)" you@example.com
else
    /usr/sbin/logwatch --output mail
fi
chmod +x /etc/cron.daily/00logwatch

logwatch --output stdout を一度実行して出力が空かどうかを判定し、空なら「EMPTY REPORT」という件名の警告メールを送る。空でなければ従来どおり --output mail でレポートを送信する。Logwatchを2回走らせることになるが、cron.daily の日次処理なので無視できる範囲だ。

まとめ

  • 現象:特定の1日分だけLogwatchメールが届かない
  • 直接原因:Logwatchが空レポートを生成し、メール送信をスキップした
  • 推定根本原因:journalローテーション(04:05)直後の一時的な不整合により、06:25のLogwatch実行時に journalctl --since yesterday --until today が前日分を返さなかった
  • 対策:空レポート時に警告メールを送るラッパースクリプトに変更し、再発を検知可能にした

「メールが届かない」系のトラブルは送信側(Postfix・DNS・SPF/DKIM)を疑いがちだが、今回は そもそもメールが生成されていなかった というオチだった。journald環境でLogwatchを使っている場合、ローテーションのタイミング次第で同種の取りこぼしが起こりうる点は頭の隅に置いておきたい。

けっきょく

うーむ…

追記:続報——空レポートの本当の原因

対策スクリプトを導入した後も引き続き空レポートになっていた。journaldローテーションとの競合は「きっかけ」に過ぎず、より根本的な原因が別にあった。

規則性が無いだろうか?と通知内容を眺めていると、dpkgのアップデートの有る無しが関与しているように思えてきた。

原因:PRINTINGフラグの連鎖依存

Logwatchの内部では、いずれかのサービスが最初に出力を返したタイミングで PRINTING=y フラグが立つ仕組みになっている。zz-disk_space(df の結果を出力するサービス)はこのフラグが立っていないと出力を行わない。

つまり dpkgのアップデートがない日は、dpkgサービスが無出力 → PRINTING フラグが立たない → zz-disk_space も無出力 → 全サービスが連鎖して空レポート、という構造になっていた。なぜここでディスク容量に着目?となると、常に一定量がカウントされる対象として適当だったから。

対策

/etc/logwatch/conf/services/zz-disk_space.conf を作成し、diskfull_threshold を1に設定する。これにより diskfullチェックが PRINTING フラグに依存せず常に実行されるため、最低限の出力が保証される。

mkdir -p /etc/logwatch/conf/services/
cat > /etc/logwatch/conf/services/zz-disk_space.conf << 'EOF'
$diskfull_threshold = 1
EOF

これでdpkgのアップデートがない日でもLogwatchが空レポートにならなくなった。

sponsored links
sponsored links
sponsored links