Shore Up
A clerk at a desk reading through a tall stack of delivery receipts by lamplight, writing a single one-page summary, and dropping it into an outgoing mail tray.
LinuxMail

Email a Daily Postfix Delivery Summary with Bash

Ketan Aagja8 min read
No ratings yet

Before you run this

This guide sets up a small bash script that reads yesterday's Postfix log, runs it through pflogsumm to build a delivery summary (messages received/delivered/deferred/ bounced, top senders, deferral reasons), and emails that summary to you once a day from cron.

The script itself is read-only against your system: it reads the mail log and sends an email. It does not modify Postfix, delete mail, or touch the queue. The only things that change your system are the two setup steps you do by hand — installing the pflogsumm package and adding a cron job — and both are trivially reversible (shown at the end).

Privileges. On a default Debian/Ubuntu install, /var/log/mail.log is readable only by root and members of the adm group. So the script must run as root (via root's crontab or /etc/cron.d) or as a user you have added to the adm group. Installing the package needs sudo. Sending the mail needs no special privilege — it uses the local Postfix you already run.

Test first. Read the script before you install it. Run it once by hand and send the report to your own address before you wire it into cron, so you can see the output and confirm mail actually leaves the box. There is nothing destructive here, but a cron job that silently fails is worse than no cron job.

Environment I assume. Debian 12 or Ubuntu 22.04/24.04, Postfix installed, and rsyslog writing plain-text logs to /var/log/mail.log. If your host is journald-only (no rsyslog, so no /var/log/mail.log), this approach does not apply as written — pflogsumm reads text log files, not the journal. On RHEL/Alma the log lives at /var/log/maillog and the package may need EPEL; adjust the path and install source.

Install pflogsumm

pflogsumm is the long-standing standard tool for exactly this job, so I use it rather than hand-rolling awk. On Debian/Ubuntu:

sudo apt update
sudo apt install pflogsumm

Confirm it is there and see what options it accepts:

pflogsumm --help
man pflogsumm

I only rely on a few options below (-d yesterday and --problems-first). pflogsumm has many detail/top-N switches; rather than list flags from memory, check the man page for the exact names before you add them.

Try it once, by hand

Before scripting anything, prove the pieces work. As root (or an adm-group user):

# Build a summary of yesterday's activity from the mail log
pflogsumm -d yesterday /var/log/mail.log | less

-d yesterday restricts the report to the previous calendar day, which is what you want for a report that runs in the early morning. If you see a sane summary, you are ready to script it.

One caveat about log rotation. -d yesterday only finds yesterday's lines that are still in /var/log/mail.log. On the morning logrotate runs, some of yesterday may already have moved to mail.log.1 (and older days are gzipped to .gz). Debian rotates mail logs weekly by default, so on six mornings out of seven yesterday is fully present. Schedule the report for early morning and this is rarely a problem; if you want to be thorough you can feed pflogsumm both files on the day of rotation, but keep the simple version unless you hit a gap.

The script

Save this as /usr/local/bin/postfix-daily-summary.sh. Edit the four values in the CONFIG block — the placeholders admin@example.com, postfix-report@example.com, and the address domain are yours to replace.

#!/usr/bin/env bash
#
# postfix-daily-summary.sh
# Emails a pflogsumm summary of yesterday's Postfix activity.
# Read-only against the system; run from root's cron (needs to read the mail log).

set -euo pipefail

# ----- CONFIG: edit these -------------------------------------------------
MAILLOG="/var/log/mail.log"                 # /var/log/maillog on RHEL/Alma
RECIPIENT="admin@example.com"               # who receives the report
SENDER="postfix-report@example.com"         # From: address on the report
SENDMAIL="/usr/sbin/sendmail"               # provided by Postfix
# --------------------------------------------------------------------------

REPORT_DATE="$(date -d 'yesterday' +%F)"    # GNU date; e.g. 2025-02-14
HOSTNAME_FQDN="$(hostname -f)"

# Fail early and loudly if the inputs aren't there.
[[ -r "$MAILLOG" ]] || { echo "Cannot read $MAILLOG (run as root?)" >&2; exit 1; }
command -v pflogsumm >/dev/null || { echo "pflogsumm not installed" >&2; exit 1; }

# Build the report. --problems-first puts deferrals/bounces at the top.
REPORT="$(pflogsumm -d yesterday --problems-first "$MAILLOG")"

# Hand a complete RFC-style message to Postfix. -t reads To:/From: from headers.
{
  printf 'From: %s\n' "$SENDER"
  printf 'To: %s\n' "$RECIPIENT"
  printf 'Subject: Postfix summary for %s — %s\n' "$HOSTNAME_FQDN" "$REPORT_DATE"
  printf 'Content-Type: text/plain; charset=UTF-8\n'
  printf '\n'                                # blank line ends the headers
  printf '%s\n' "$REPORT"
} | "$SENDMAIL" -t

Notes on the non-obvious lines:

  • set -euo pipefail makes the script stop on any error or unset variable, so a cron failure is a real failure rather than a silent empty email.
  • date -d 'yesterday' is a GNU coreutils feature and is standard on Debian/Ubuntu.
  • Piping to /usr/sbin/sendmail -t is Postfix's own Sendmail-compatible interface; the binary is always present when Postfix is installed, so there is no extra mailutils dependency. If you prefer the mail/mailx command instead, install mailutils — but the sendmail approach needs nothing extra.

Make it executable:

sudo chmod 750 /usr/local/bin/postfix-daily-summary.sh

Now run it once by hand and check your inbox:

sudo /usr/local/bin/postfix-daily-summary.sh

Schedule it

Run it from root's crontab so it can read the log. Open root's crontab:

sudo crontab -e

Add one line — this runs at 06:15 every morning:

15 6 * * * /usr/local/bin/postfix-daily-summary.sh

Cron mails root any output the job produces on stderr. Because of set -e, a broken run (missing log, uninstalled package, sendmail failure) will produce output and you will hear about it — which is exactly what you want from a monitoring job.

If you would rather keep the schedule in a file, drop an equivalent line into /etc/cron.d/postfix-daily-summary using the standard cron.d format (which includes a user field). Pick one method; don't do both.

Verify it worked

  1. Run it manually and read the report. The command above should land an email in RECIPIENT's inbox within a minute.

  2. Confirm Postfix accepted and delivered your report. Watch the log as you send:

    sudo tail -f /var/log/mail.log
    

    You should see the report message queued and a status=sent (or a status=deferred with a reason if your relay/DNS is off). If the report can't be delivered, fix that before trusting the daily job.

  3. Confirm the cron job is registered:

    sudo crontab -l
    
  4. Confirm it fired the next morning. After the first scheduled run, check the cron log for the entry:

    grep postfix-daily-summary /var/log/syslog
    

    (On systems that log cron to the journal, use journalctl -u cron --since yesterday.)

Undo

Nothing here is hard to reverse.

  • Stop the daily report: sudo crontab -e and delete the line (or sudo rm /etc/cron.d/postfix-daily-summary if you used that method).
  • Remove the script: sudo rm /usr/local/bin/postfix-daily-summary.sh.
  • Remove the tool: sudo apt remove pflogsumm if you no longer want it.

That is the whole thing: a read-only parser, a plain-text email, and one cron line. Keep the script simple and let pflogsumm do the parsing — it already handles the log-format quirks you'd otherwise reinvent in awk.

Written by
Ketan Aagja

Runs enterprise networks and security for a living, and writes Shore Up to turn two decades of hands-on Linux, Windows and mail-server work into guides you can actually use.

More about the author →

Was this article helpful?

Tap a star — no sign-in needed.

Be the first to rate this article.