
Alert on a Growing Postfix Mail Queue with a Cron Script
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.
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.
