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
| Cause | What it looks like | First thing to check |
|---|---|---|
| Backscatter from accepted spam | Inbound spam with forged sender addresses; bounces generated to those forged addresses | grep 'status=bounced' /var/log/mail.log | head -20 | grep -o 'from=<[^>]*>' | sort | uniq -c |
| Autoresponder or vacation loop | Bounce messages triggering auto-replies that re-enter the queue as new mail | Check autoresponder logs; look for MAILER-DAEMON as trigger in autoresponder output |
| Wildcard alias forwarding to invalid address | Catch-all alias accepts everything, forwards to external address that bounces | postconf -h virtual_alias_maps and inspect for catch-all patterns (@domain) |
| Mailing list VERP escaping bug | Bounces from list delivery have malformed VERP return paths that bounce again | Check 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
- 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 | headon Postfix 3.1+ for a clean sender breakdown.
Identify the original bounce source. Parse recent bounce entries to find what mail is generating the bounces. Look at the
from=<...>field instatus=bouncedlog 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.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.
Check for resource pressure. Run
df -i /var/spool/postfiximmediately. Inode exhaustion is the most common secondary failure in bounce storms because each queued message is a separate file. Also checkdf -h /var/spool/postfixfor disk space. Look forNo space left on devicein the mail log, which can indicate inode exhaustion even when disk space appears available.Verify the double-bounce safety net. Confirm that
double_bounce_senderhas 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.Check notify_classes for visibility. The default is
resource, software, which does not includebounceor2bounce. If you were not alerted to this problem by Postfix itself, consider adding2bouncetonotify_classesso the postmaster receives double-bounce notifications going forward.
Metrics and signals to monitor
| Signal | Why it matters | Warning sign |
|---|---|---|
| Queue entries by sender | Detects MAILER-DAEMON / double-bounce dominance early | MAILER-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 resources | Bounce rate exceeds legitimate injection rate |
| Maildrop queue depth | Bounces are local submissions; maildrop growth indicates bounce generation load | Any sustained growth above baseline |
| Inode usage on queue filesystem | Each queued message is a file; bounce storms exhaust inodes before disk space | Free inodes below 10% |
| Deferred queue growth rate | Velocity matters more than absolute size; growing deferred with MAILER-DAEMON senders confirms the loop | Sustained positive growth over 1 or more hours |
| Pickup daemon activity | Pickup processes maildrop entries; heavy activity confirms local submission burst | Pickup 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_recipientinsmtpd_recipient_restrictionsand maintain accuraterelay_recipient_mapsorlocal_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=bouncedrate 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 -hshows free space. Monitordf -ion the queue filesystem.Add 2bounce to notify_classes. The default is
resource, software. Add2bounceto 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_senderto 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=bouncedevents, 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.
Related guides
- Postfix active queue saturation: hitting qmgr_message_active_limit
- Postfix bounce rate spike: 5xx failures, bad address lists, and reputation risk
- Postfix check warnings: configuration drift and permission problems
- Postfix Connection refused: blocked port 25 and rejected outbound delivery
- Postfix Connection timed out: delivery deferrals to unreachable destinations
- Postfix content_filter backpressure: incoming queue growth when Amavis or Rspamd slows
- Postfix deferred queue growing: why mail piles up and how to drain it
- Postfix destination concurrency limit: tuning per-destination delivery
- Postfix DNS resolver failure: when a broken resolver defers mail to everyone
- Postfix flushing and clearing the deferred queue: postqueue and postsuper
- Postfix greylisting delays: 450 4.7.1 deferrals and slow first delivery
- Postfix Host or domain name not found: DNS name service errors deferring mail






