この記事の見出し一覧
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が空レポートにならなくなった。
※本記事は当サイト管理人の個人的な備忘録です。情報内容には配慮しておりますが、本記事の参照または付随ソースコード利用後にいかなる損害が発生しても、当サイト及び管理人はいっさいの責任を負いません。

