Shore Up
A stack of letters piling higher and higher in a sorting tray while a small warning lamp lights up on the desk beside it.
MailLinux

Alert on a Growing Postfix Mail Queue with a Cron Script

Ketan Aagja9 min read
No ratings yet

A mail queue that quietly grows is one of those failures you notice late — usually when a user asks why their mail from three hours ago hasn't arrived. A backlog can mean a dead relay host, a DNS problem, a downstream server rejecting everything, or an outbound spam run from a compromised account. This guide sets up a small cron job that counts the queue and emails you when it crosses a threshold, so you hear about it early.

Before you run this

What it does: the script asks Postfix how many messages are currently queued, and if that number is at or above a threshold you set, it sends you a single warning email. It only reads the queue — it never flushes, deletes, or requeues anything. The one change it makes on disk is a small state file (under /var/tmp) so it doesn't email you on every run while the queue stays high.

Privileges: you do not need root to count the queue. The postqueue -p command is designed to be run by ordinary users. What you do need is a working way to send the alert mail — the mail command from the bsd-mailx or mailutils package — and a Postfix that can actually deliver that alert (if Postfix itself is the thing that's broken, the alert may queue too; see the note at the end). Install the cron job under whichever account has a mailer available; root is convenient but not required.

Test first: read the script before you install it. Run it by hand once, then temporarily set the threshold to 0 or 1 so it fires, and confirm the email actually lands in your inbox. Do this on a test or staging box before you trust it on a production mail server. Nothing here is destructive, but you want to prove the alerting path works while things are calm — not discover it's broken during an incident.

Assumptions: Debian 12 or Ubuntu 22.04 with Postfix already installed and running, bash, and the classic cron daemon (/etc/crontab, crontab -e). On RHEL/Alma the commands are identical; only the mail-client package name differs (mailx there), and cron is provided by cronie. The queue-counting works the same on any Postfix version.

Confirm you can read the queue

Before scripting anything, check the command by hand:

postqueue -p

You'll get either Mail queue is empty or a list of messages ending in a summary line that looks like this:

-- 5 Kbytes in 42 Requests.

That trailing Requests count is the number we care about, and — importantly — it's correct whether or not your Postfix uses the newer long queue IDs. That's why the script parses the summary line instead of trying to count individual entries with a regex that could miss one ID format or the other.

Also confirm you can send a test mail:

echo "test body" | mail -s "test from $(hostname -s)" you@example.com

Replace you@example.com with a real address you can check. If that arrives, you're set. If mail isn't found, install it:

# Debian/Ubuntu
sudo apt-get install bsd-mailx
# RHEL/Alma
sudo dnf install mailx

The script

Save this as /usr/local/bin/postfix-queue-alert.sh. Change the three settings at the top to your own values — the admin@example.com address and the threshold are the two you'll actually tune.

#!/usr/bin/env bash
# Alert when the Postfix mail queue grows past a threshold.
set -euo pipefail

THRESHOLD=100                       # queued messages that triggers an alert — tune this
ALERT_TO="admin@example.com"        # who gets the warning — replace with your address
HOST_SHORT="$(hostname -s)"
STATE_FILE="/var/tmp/postfix_queue_alert.state"  # remembers if we're already alerting

# Ask Postfix how many messages are queued.
queue_output="$(postqueue -p)"

if printf '%s\n' "$queue_output" | grep -q "Mail queue is empty"; then
  count=0
else
  # Summary line looks like:  -- 5 Kbytes in 42 Requests.
  # Field 5 is the message count, in both short- and long-queue-ID formats.
  count="$(printf '%s\n' "$queue_output" | tail -n 1 | awk '{print $5}')"
fi

# Fall back to 0 if parsing produced anything non-numeric.
if ! [[ "$count" =~ ^[0-9]+$ ]]; then
  count=0
fi

already_alerted=0
if [[ -f "$STATE_FILE" ]]; then
  already_alerted=1
fi

if (( count >= THRESHOLD )); then
  if (( already_alerted == 0 )); then
    printf 'Postfix queue on %s is %d messages (threshold %d).\n' \
      "$HOST_SHORT" "$count" "$THRESHOLD" \
      | mail -s "Postfix queue high on ${HOST_SHORT}: ${count}" "$ALERT_TO"
    touch "$STATE_FILE"   # mark that we've alerted so we don't email every run
  fi
else
  # Queue is back to normal — clear the flag so the next spike alerts again.
  rm -f "$STATE_FILE"
fi

Make it executable:

sudo chmod +x /usr/local/bin/postfix-queue-alert.sh

The state file is the piece worth understanding. Without it, a queue stuck above the threshold would email you every single time cron runs. With it, you get one email when the queue crosses the line, and it stays quiet until the queue drops back below the threshold — at which point the flag clears and a future spike can alert again. It's a deliberately simple "one alert per incident" behaviour, not a full flap-dampening system.

Schedule it

Run it every five minutes. Edit the crontab for the account you chose:

crontab -e

Add:

# Check the Postfix queue every 5 minutes and alert if it's backing up
*/5 * * * * /usr/local/bin/postfix-queue-alert.sh

Five minutes is a sane default: frequent enough to catch a backlog while it's small, rare enough that it's no load. Don't set the threshold too low — a busy server can legitimately show dozens of transient messages. Watch your normal postqueue -p counts for a day or two and set the threshold comfortably above your usual peak.

Verify it works

First, run it once by hand and check the exit status:

/usr/local/bin/postfix-queue-alert.sh; echo "exit: $?"

Then force an alert by temporarily editing the script to set THRESHOLD=0, run it again, and confirm the email arrives. Put the real threshold back afterwards.

To confirm cron is actually running the job, watch the cron log:

# Debian/Ubuntu
grep postfix-queue-alert /var/log/syslog
# RHEL/Alma
grep postfix-queue-alert /var/log/cron

You should see the command being invoked on your schedule.

Undo / roll back

There's nothing destructive to reverse, but to remove the whole thing cleanly:

crontab -e                                   # delete the */5 line, save
sudo rm /usr/local/bin/postfix-queue-alert.sh
rm -f /var/tmp/postfix_queue_alert.state

One honest caveat

If Postfix itself is completely down or unable to deliver any mail, the alert email will just join the queue it's warning you about — you won't receive it. This script is good at catching the common case (a growing backlog while Postfix is otherwise up), not a total outage. For that, point the alert at an external mail path or a separate monitoring channel, and lean on your uptime monitoring for "is the service alive at all". For queue-management details — flushing, holding, deleting messages — see the postqueue and postsuper manual pages, and the queue-management material in the official Postfix documentation.

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.