The Postfix queue is growing fast and postqueue -p output is dominated by messages from MAILER-DAEMON or double-bounce@<your hostname>. The count climbs steadily despite no corresponding increase in legitimate mail volume. Queue slots, disk I/O, and inodes are being consumed by bounce generation, and legitimate mail is getting delayed or stuck behind the noise.

Left unchecked, the loop can exhaust inodes on the queue filesystem (each queued message is a separate file), starve the queue manager of active delivery slots, and delay legitimate mail for hours.

What this means

A bounce (delivery status notification) is generated when a delivery attempt fails permanently with a 5xx response, or when a message exceeds its lifetime in the queue. Postfix sends the bounce to the envelope sender of the original message. When that sender address is forged, invalid, or undeliverable, the bounce fails too. Postfix then generates a double-bounce, addressed to 2bounce_notice_recipient (default: postmaster).

Postfix has a built-in safeguard against infinite recursion. The double-bounce sender address (double_bounce_sender, default double-bounce) acts as a marker: when the bounce daemon encounters a failed delivery whose sender matches this address, it discards the message rather than generating another bounce. This prevents true infinite loops.

The problem is that “discarded” still costs resources. Each bounce and double-bounce passes through queue file creation, the bounce daemon, the cleanup daemon, and the queue manager before being discarded. At high volume (thousands of bounces per minute from a backscatter flood or a misconfigured autoresponder), this generation load saturates CPU, burns disk I/O, and consumes inodes during the window each file exists.

The loop becomes truly self-sustaining when something re-injects bounces back into the system. Common culprits: an autoresponder or vacation program that replies to MAILER-DAEMON messages, a wildcard alias that forwards spam to an invalid external address, or a mailing list manager with a VERP formatting bug that creates bounce loops.

flowchart TD
    A["Inbound mail with forged sender"] --> B["Delivery attempt fails: 5xx"]
    B --> C["Bounce generated to sender"]
    C --> D{"Sender deliverable?"}
    D -->|No| E["Double-bounce generated"]
    E --> F["Postfix discards if sender is double-bounce address"]
    D -->|Autoresponder triggers| G["Auto-reply re-enters queue"]
    G --> H["New bounce iteration"]
    H --> B
    F --> I["Generation load consumed: CPU, I/O, inodes"]

Common causes

CauseWhat it looks likeFirst thing to check
Backscatter from accepted spamInbound spam with forged sender addresses; bounces generated to those forged addressesgrep 'status=bounced' /var/log/mail.log | head -20 | grep -o 'from=<[^>]*>' | sort | uniq -c
Autoresponder or vacation loopBounce messages triggering auto-replies that re-enter the queue as new mailCheck autoresponder logs; look for MAILER-DAEMON as trigger in autoresponder output
Wildcard alias forwarding to invalid addressCatch-all alias accepts everything, forwards to external address that bouncespostconf -h virtual_alias_maps and inspect for catch-all patterns (@domain)
Mailing list VERP escaping bugBounces from list delivery have malformed VERP return paths that bounce againCheck list manager bounce logs; inspect VERP localpart length (over 64 characters can fail)

Quick checks

# Count MAILER-DAEMON / double-bounce messages in queue
postqueue -p | grep -cE 'MAILER-DAEMON|double-bounce'

# Check maildrop queue for local submission burst (bounces are local submissions)
ls /var/spool/postfix/maildrop/ | wc -l

# Check inode availability on queue filesystem (critical during bounce storms)
df -i /var/spool/postfix

# Identify top bounced senders (usually forged addresses)
grep 'status=bounced' /var/log/mail.log | grep -o 'from=<[^>]*>' | sort | uniq -c | sort -rn | head -20

# Count bounce entries in the current log minute (repeat over several minutes for rate)
grep "$(date '+%b %e %H:%M')" /var/log/mail.log | grep -c 'status=bounced'

# Verify double-bounce and notification configuration
postconf -h double_bounce_sender 2bounce_notice_recipient bounce_queue_lifetime notify_classes

# Check queue breakdown by directory
echo "incoming: $(find /var/spool/postfix/incoming -type f | wc -l)"
echo "active: $(find /var/spool/postfix/active -type f | wc -l)"
echo "deferred: $(find /var/spool/postfix/deferred -type f | wc -l)"
echo "maildrop: $(find /var/spool/postfix/maildrop -type f | wc -l)"

On RHEL-based systems, the mail log is typically /var/log/maillog rather than /var/log/mail.log.

How to diagnose it

  1. Confirm the bounce loop pattern. Check whether MAILER-DAEMON and double-bounce senders dominate the queue. If more than half the queue entries are from these senders, you are in a bounce storm. Use postqueue -j | jq -r '.[].sender' | sort | uniq -c | sort -rn | head on Postfix 3.1+ for a clean sender breakdown.
  1. Identify the original bounce source. Parse recent bounce entries to find what mail is generating the bounces. Look at the from=<...> field in status=bounced log lines. Forged addresses (random strings, nonexistent domains, or addresses at the recipient domain) indicate backscatter. Legitimate-looking addresses indicate a routing or data quality problem.

  2. Distinguish external trigger from internal loop. If the maildrop queue is active and growing, bounces are being generated locally by Postfix itself. If the incoming queue is the growth point, external mail is arriving and generating bounces on delivery failure. A maildrop queue growing in lockstep with bounce rate suggests an autoresponder or local process re-injecting bounces.

  3. Check for resource pressure. Run df -i /var/spool/postfix immediately. Inode exhaustion is the most common secondary failure in bounce storms because each queued message is a separate file. Also check df -h /var/spool/postfix for disk space. Look for No space left on device in the mail log, which can indicate inode exhaustion even when disk space appears available.

  4. Verify the double-bounce safety net. Confirm that double_bounce_sender has not been modified from its default (double-bounce) and that the double-bounce address is not a valid deliverable mailbox. Making the double-bounce address deliverable creates a transport loop. The address should be an unconditional black hole.

  5. Check notify_classes for visibility. The default is resource, software, which does not include bounce or 2bounce. If you were not alerted to this problem by Postfix itself, consider adding 2bounce to notify_classes so the postmaster receives double-bounce notifications going forward.

Metrics and signals to monitor

SignalWhy it mattersWarning sign
Queue entries by senderDetects MAILER-DAEMON / double-bounce dominance earlyMAILER-DAEMON senders exceed 10% of total queue
Bounce rate (status=bounced per minute)Sustained high bounce rate indicates data quality or routing problem, and consumes resourcesBounce rate exceeds legitimate injection rate
Maildrop queue depthBounces are local submissions; maildrop growth indicates bounce generation loadAny sustained growth above baseline
Inode usage on queue filesystemEach queued message is a file; bounce storms exhaust inodes before disk spaceFree inodes below 10%
Deferred queue growth rateVelocity matters more than absolute size; growing deferred with MAILER-DAEMON senders confirms the loopSustained positive growth over 1 or more hours
Pickup daemon activityPickup processes maildrop entries; heavy activity confirms local submission burstPickup process count elevated or maildrop files aging

Fixes

Stop the incoming source

If backscatter is the trigger (inbound spam with forged senders generating bounces), tighten recipient validation before accepting mail. Review your current restrictions first, then add reject_unlisted_recipient:

# Review current restrictions before changing anything
postconf -h smtpd_recipient_restrictions

# WARNING: postconf -e overwrites the entire parameter. Prepend
# reject_unlisted_recipient to your EXISTING restrictions, do not
# copy-paste the example below blindly.
postconf -e 'smtpd_recipient_restrictions = reject_unlisted_recipient, reject_unauth_destination'
postfix reload

reject_unlisted_recipient rejects recipients not found in local_recipient_maps, virtual_alias_maps, virtual_mailbox_maps, or relay_recipient_maps before the message enters the queue. This prevents Postfix from accepting mail for recipients it cannot deliver, eliminating the bounce at the source. If your deployment uses relay_recipient_maps, ensure the map is current and covers all valid recipients.

Disable or fix autoresponders and vacation programs

If an autoresponder is replying to MAILER-DAEMON messages, it is re-injecting bounces into the queue. Disable the autoresponder immediately to break the loop, then configure it to skip messages from MAILER-DAEMON, postmaster, and other automated senders. Most vacation autoresponders have an exclusion list for this purpose.

Hold before deleting

Before bulk-deleting messages, hold the MAILER-DAEMON entries so you can investigate patterns without the queue continuing to grow:

# Hold MAILER-DAEMON messages for investigation (reversible)
postqueue -p | awk '/MAILER-DAEMON/ {print $1}' | sed 's/[*!]$//' | postsuper -h -

Held messages stop consuming active queue slots and delivery attempts while you investigate. You can release them later with postsuper -H or delete them with postsuper -d once you understand the pattern.

Clear the queue carefully

Once you have confirmed the pattern and addressed the source, remove the bounce messages:

# DESTRUCTIVE: Deletes matched messages from the queue. Irreversible.
# Run mailq | grep -E 'MAILER-DAEMON|double-bounce' FIRST and verify
# the matched queue IDs are actually bounce messages before deleting.
mailq | grep -E 'MAILER-DAEMON|double-bounce' | awk '{print $1}' | sed 's/[*!]$//' | postsuper -d -

The sed 's/[*!]$//' strips queue status indicators (* for active, ! for held) that mailq appends to queue IDs, because postsuper expects bare queue IDs.

Fix mailing list VERP issues

If a mailing list manager (such as Sympa, Mailman, or similar) is generating bounces with malformed VERP return paths, check for VERP localpart length exceeding 64 characters. Some mail systems reject overly long localparts, causing the bounce notification itself to bounce. Update the list manager or adjust VERP format settings to keep the localpart within limits.

Prevention

  • Reject unknown recipients before queueing. Use reject_unlisted_recipient in smtpd_recipient_restrictions and maintain accurate relay_recipient_maps or local_recipient_maps. This is the single most effective backscatter prevention measure.

  • Monitor bounce rate explicitly. Bounces are counted as “sent” in simple delivery metrics. Set up explicit monitoring for status=bounced rate and alert when it exceeds your baseline (typically under 1% for transactional mail, under 0.5% for marketing).

  • Monitor inodes, not just disk space. Postfix creates one file per queued message plus metadata files. Inode exhaustion manifests as “No space left on device” even when df -h shows free space. Monitor df -i on the queue filesystem.

  • Add 2bounce to notify_classes. The default is resource, software. Add 2bounce to receive double-bounce notifications, giving you early warning before a loop saturates the queue.

  • Configure autoresponders to exclude automated senders. Ensure vacation programs, ticketing systems, and auto-responders skip MAILER-DAEMON, postmaster, and other bounce or notification addresses.

  • Verify the double-bounce address is a black hole. Never make the double-bounce address a valid deliverable mailbox. It must be an unconditional discard point to prevent transport loops. Setting double_bounce_sender to an empty string causes a fatal error in Postfix; always use a non-empty value.

How Netdata helps

  • Per-second queue metrics let you detect bounce-driven queue growth within seconds rather than minutes, and correlate growth with mail flow velocity (injected vs delivered rate) in the same view.

  • Maildrop queue depth is surfaced alongside active, deferred, and incoming queues, so you can immediately see whether the bounce storm is driven by local submissions (maildrop growth) or external injection (incoming growth).

  • Bounce rate anomaly detection flags sudden spikes in status=bounced events, catching the bounce storm before it saturates the queue or exhausts inodes.

  • Inode utilization is tracked alongside disk space, so you catch the secondary failure mode (inode exhaustion) that most teams miss during bounce storms.

  • Correlating bounce rate with queue growth, maildrop activity, and inode usage in a single dashboard shortens the path from symptom to root cause, letting you confirm the loop pattern and act before legitimate mail is affected.