Postfix уже пишет в /var/log/mail.log каждую SMTP-транзакцию: кто отправил, кому, за сколько байт, с каким финальным статусом и какой код вернул принимающий сервер. Данных достаточно, чтобы поймать проблему за сутки до того, как о ней сообщит клиент. Но в реальности логи открывают в двух случаях: когда пришла жалоба «письмо не дошло» и когда Gmail начал ставить 421 4.7.0. К этому моменту вы уже отстаёте от проблемы на несколько часов, а grep по 200 тысячам строк — это гадание, а не диагностика.
Ниже — как заменить «грепаю руками, когда горит» на дисциплину: суточный отчёт pflogsumm на почту через cron и живой дашборд GoAccess поверх тех же логов. Двадцать минут настройки, дальше три числа каждое утро.
Что реально живёт в mail.log и почему grep не масштабируется
Ключевая деталь, которую упускают: одно письмо — это не одна строка лога, а три-шесть. Postfix пишет по мере прохождения письма через свои демоны, и склеивает их сквозной queue ID — шестнадцатеричный идентификатор вроде 4Xk2p13Nzz.
smtpd[2011]: 4Xk2p13Nzz: client=mail.example.com[203.0.113.9]
cleanup[2015]: 4Xk2p13Nzz: message-id=<[email protected]>
qmgr[1180]: 4Xk2p13Nzz: from=<[email protected]>, size=4021, nrcpt=1
smtp[2020]: 4Xk2p13Nzz: to=<[email protected]>, relay=gmail-smtp-in.l.google.com[142.250....]:25, delay=1.4, status=sent (250 2.0.0 OK)Отсюда следует неприятный вывод: grep -c 'status=bounced' mail.log считает не письма, а события доставки. Письмо на три получателя, где двое отбились, даст два bounced — но письмо было одно. При ретраях deferred инкрементируется каждую попытку. Поэтому ручной grep систематически завышает и врёт в цифрах, на которые вы потом принимаете решения.
Второй подвох 2026 года: минимальные и облачные образы свежих дистрибутивов (Debian 13, Ubuntu 24.04) всё чаще идут без rsyslog — логи оседают только в journald, и файла /var/log/mail.log попросту нет. Либо ставите rsyslog, либо выгружаете журнал для анализатора:
journalctl --facility=mail --since yesterday --no-pager > /tmp/mail.logНа RHEL/Alma путь другой — /var/log/maillog. Дальше по тексту подставляйте свой.
pflogsumm: суточный отчёт за одну команду
pflogsumm — Perl-скрипт без демона, зависит от Date::Calc, ничего не крутится в фоне. Ставится тривиально:
apt install pflogsumm # Debian/Ubuntu
dnf install postfix-perl-scripts # RHEL/Alma (пакет с тем же скриптом)Базовый прогон по вчерашнему логу:
pflogsumm -d yesterday /var/log/mail.logШапка отчёта — это те самые цифры, ради которых всё затевалось:
Grand Totals
------------
messages
14201 received
13980 delivered
12 forwarded
402 deferred
88 bounced
143 rejected
0 reject warnings
0 held
0 discardedЧитаются они так. received — сколько приняли в очередь. delivered — реально ушло с кодом 2xx. deferred — временно не смогли (4xx), письмо ещё в очереди и будет ретраиться. bounced — окончательный отказ (5xx), отправителю ушёл bounce. rejected — отбито прямо на нашем smtpd (спам-фильтр, RBL, неизвестный получатель на входящем) — это про входящую почту, не путайте с bounced на исходящей.
Ниже шапки идут разделы, которые и делают инструмент диагностическим. Флаг --problems-first поднимает их наверх, --verbose-msg-detail разворачивает конкретные адреса:
pflogsumm --problems-first --verbose-msg-detail --rej-add-from \
-d yesterday /var/log/mail.log.1Что смотреть в этих секциях:
- message deferral detail — сгруппированные причины отсрочек. Здесь видно, что 380 из 402 deferred — это
connection timed outк одному релею. Это не ваша проблема с контентом, это сеть или репутация IP. - message bounce detail — окончательные отказы с текстом от принимающей стороны:
550 5.1.1 User unknown,552 5.2.2 Mailbox full. Отсюда растут списки невалидных адресов. - Warnings — предупреждения Postfix, часто первый признак проблем с TLS или DNS.
- senders by message count / recipients by message count — топы. Аномальный отправитель с тысячами писем за ночь — это либо легитимная рассылка, либо скомпрометированный аккаунт.
Полезные флаги для стабильных отчётов: --zero-fill заполняет пустые часы нулями (иначе почасовая сводка «прыгает» и её нельзя сравнивать день к дню), --iso-date-time даёт ISO-даты, -q глушит лишний вывод.
Ежедневный отчёт на почту через cron
Смысл в том, чтобы отчёт приходил сам, а не когда вы вспомнили. Кладём в /etc/cron.d/pflogsumm:
10 4 * * * root pflogsumm -d yesterday --problems-first /var/log/mail.log.1 | mail -s "[evilmail.pro] Mail report $(date -d yesterday +\%F)" [email protected]Три тонкости, на которых спотыкаются.
Читаем `mail.log.1`, а не `mail.log`. logrotate по умолчанию крутит логи ночью. Если запустить pflogsumm в 04:10 по mail.log, вы получите неполные сутки — часть уже уехала в .1. Либо считайте mail.log.1 после ротации, либо повесьте вызов прямо в секцию postrotate в /etc/logrotate.d/rsyslog, чтобы отчёт формировался ровно по закрытому суточному файлу до его сжатия.
Осмысленный Subject. Дата в теме и, в идеале, ключевое число прямо там — тогда красную зону видно, не открывая письмо. Небольшая обёртка, которая считает bounce-rate и подсвечивает его:
#!/bin/bash
LOG=/var/log/mail.log.1
REP=$(pflogsumm -d yesterday --problems-first "$LOG")
DEL=$(grep -oP '^\s*\K\d+(?=\s+delivered)' <<<"$REP")
BNC=$(grep -oP '^\s*\K\d+(?=\s+bounced)' <<<"$REP")
RATE=$(awk "BEGIN{printf \"%.1f\", $BNC*100/($DEL+1)}")
FLAG=""; (( $(awk "BEGIN{print ($RATE>5)}") )) && FLAG="[ALERT] "
echo "$REP" | mail -s "${FLAG}[evilmail.pro] $(date -d yesterday +%F) bounce=${RATE}%" [email protected]Не ловите рекурсию. Отчётное письмо само проходит через Postfix и оседает в логах. На объёме evilmail это шум в пределах погрешности, но если гоняете отчёты по нескольким доменам — фильтруйте служебный трафик, иначе postmaster@ начнёт попадать в топ-получателей.
GoAccess поверх почтовых логов: живой дашборд
pflogsumm — это утренняя газета: раз в сутки, для человека. Когда идёт 4xx-шторм, хочется смотреть в реальном времени и кликать. Для этого — GoAccess.
Честное ограничение: GoAccess создан для web access-логов и Postfix из коробки не понимает. Никакого пресета --log-format=postfix нет. Нужно вручную описать формат под конкретные строки вашего syslog. Это самая хрупкая часть связки — поля Postfix различаются между smtpd, qmgr и smtp, поэтому парсер придётся подгонять итеративно, глядя на реальные строки.
Отправная точка для строки доставки:
goaccess /var/log/mail.log -o /var/www/html/mailstat.html \
--date-format='%b %d' \
--time-format='%H:%M:%S' \
--log-format='%d %t %^ postfix/%^[%^]: %^: %h from=<%^> to=<%^>, %^ status=%s'Где %d — дата, %t — время, %h берём как «хост» для группировки, %s — статус. %^ — пропускаемые поля. Реальную строку почти наверняка придётся дошлифовать под ваш формат syslog (приоритет, hostname, PID), но принцип такой.
Живой режим с WebSocket-обновлением:
goaccess /var/log/mail.log -o /var/www/html/live.html \
--real-time-html --ws-url=wss://stat.evilmail.pro:7890 --port=7890 \
--date-format='%b %d' --time-format='%H:%M:%S' --log-format='...'Понадобится открытый WS-порт и reverse proxy с TLS перед ним — статику отдаёт nginx, а сокет проксируется на 7890. Взамен получаете дашборд, который обновляется по мере роста лога: топ-отправители, топ-получатели, распределение статусов, всплески по часам. Там, где pflogsumm даёт агрегат, GoAccess даёт drill-down — кликнуть на всплеск в 03:00 и увидеть, кто именно его создал. Слабое место обратное: GoAccess не знает семантики «bounce vs defer», для него это просто строки со статусом. Смысловую разбивку по-прежнему даёт pflogsumm.
Читаем цифры как инженер: defer, bounce, reject — три разные болезни
Главная ошибка новичка — лечить их одинаково. Это разные диагнозы.
`4xx deferred` — временно, само не смертельно. Deferred: connection timed out — принимающий сервер не ответил: сеть, их перегрузка или ваша репутация. 421 4.7.0 Try again later от Gmail/Outlook — это greylisting или rate limit по вашему IP. Лечится терпением и, если IP новый, прогревом (warmup). Одиночные deferred — норма, Postfix переретраит. Тревога — когда deferred растёт по часам к одному провайдеру: это уже репутация, а не случайность.
`5xx bounced` — окончательно. 550 5.1.1 User unknown — адреса нет, чистите список. 552 Mailbox full — на стороне получателя. Всплеск bounce от одного отправителя почти всегда означает одно из двух: скомпрометированный аккаунт рассылает спам на мусорные адреса, или кто-то залил кривой список рассылки. И то и другое бьёт по репутации домена — реагировать в тот же день.
`reject` — на входящем периметре. Ваш smtpd отбил письмо: RBL, спам-скор, неизвестный локальный получатель. Само по себе это здоровье фильтров, а не проблема доставки. Но резкий рост reject на входящем — это либо атака, либо что-то сломалось в правилах.
Связка с deliverability прямая: растёт deferred/bounced к крупным провайдерам — идём проверять аутентификацию. PTR (обратная запись IP должна резолвиться в ваш HELO-хост), SPF (v=spf1 ... включает отправляющий IP), DKIM-подпись валидна, DMARC-запись _dmarc на месте, и IP не в Spamhaus/Barracuda. Порядок именно такой — PTR и SPF ломаются чаще всего.
Пороги, по которым стоит держать себя:
- bounce-rate > 5% от delivered — красная зона. Крупные провайдеры начинают резать репутацию задолго до этого.
- deferred, растущий по часам к одному провайдеру — проблема аутентификации/репутации, а не «интернет тормозит».
- письмо в active queue дольше 4 часов — ручной разбор через
postqueue -p/mailq.
Мини-runbook: что делать каждое утро
- Открыть ночной отчёт pflogsumm. Сверить
delivered/deferred/bouncedсо вчерашними — интересны не абсолюты, а дельта и bounce-rate. - Глянуть message bounce detail, топ-3 причины.
User unknownв товарных количествах — чистка списка; всплеск от одногоfrom=— проверять аккаунт. - Открыть дашборд GoAccess, посмотреть на всплески по часам. Ночной пик без причины — повод грепнуть queue ID.
mailq | tail— что застряло. Всё старше 4 часов разбираем руками.- При аномалии — вытащить конкретную транзакцию целиком:
grep 4Xk2p13Nzz /var/log/mail.logпокажет путь письма отsmtpdдо финальногоstatus=. - Полезно под рукой:
postqueue -f
Дальше это автоматизируется: mtail или grok_exporter превращают mail.log в метрики Prometheus через textfile-collector, а Grafana строит графики и шлёт алерт, когда bounce-rate переваливает порог. pflogsumm остаётся для человека — прочитать за кофе и понять контекст, — а метрики берут на себя круглосуточную вахту. Именно так это устроено в инфраструктуре evilmail.pro: суточный дайджест для глаз плюс метрики для алертинга, и ни одна жалоба на недоставку больше не приходит раньше, чем мы сами увидели проблему в цифрах.


