๐ŸŽ New User? Get 20% off your first purchase with code NEWUSER20 ยท โšก Instant download ยท ๐Ÿ”’ Secure checkout Register Now โ†’
Menu

Categories

Exim Mail Queue Management: Commands and Troubleshooting

Exim Mail Queue Management: Commands and Troubleshooting

A user reports mail not arriving. You SSH into the server and run exim -bp: 3,000 messages in the exim mail queue, half of them frozen. Some have been sitting for days. You need to know which messages matter, why they are stuck, and how to unstick them without losing legitimate mail or triggering a flood of bounces. Exim's queue commands give you the visibility to triage, but the output is terse and the reasons cryptic.

This reference walks through the exact commands to inspect queue state, decode frozen message tags, force retries, and clear backlogs safely. Every example shows real output and the specific flags that matter. By the end you will know which messages to delete, which to retry, and how to spot the configuration or DNS issue that filled the queue in the first place.

Inspecting the Exim Queue

The exim -bp command prints the queue, one line per message. Each line shows the message age, size, ID, sender, and recipient. A typical entry looks like this:

 2d  3.4K 1sA3Bz-0002kL-7G <sender@example.com>
          recipient@domain.com

The message is two days old, 3.4 KB, and waiting for delivery. If the message is frozen, you see an asterisk and a freeze tag:

 5d  1.2K 1sA1Xy-0001pQ-2F <bounce@example.com> *** frozen ***
          original-recipient@domain.com

Run mailq as a shortcut; it is an alias for exim -bp. For a queue with thousands of messages, use exim -bpu to show only undelivered messages and skip those already processed. The full queue listing scrolls past too fast, so pipe to less or redirect to a file for analysis.

Reading exim -bp Output

Each message ID is unique and contains the arrival timestamp. The first column shows age in hours (5h), days (2d), or weeks (1w). The second column is message size. Sender and recipient addresses follow. Messages with multiple recipients list each on a separate indented line. If you see D next to a recipient, that address already received the message; the rest are pending.

Summarizing Queue Contents with exiqsumm

The exiqsumm script parses exim -bp output and groups messages by recipient domain or sender. Run it like this:

exim -bp | exiqsumm

You get a table of counts and total volume per domain:

Count  Volume  Destination
-----  ------  -----------
  842   2.1M   domain.com
  312   890K   anotherdomain.net
   87   145K   FROZEN

This tells you that 842 messages are queued for domain.com. If one domain dominates, the problem is likely on their end: their MX is down, they are greylisting aggressively, or your IP is temporarily blocked. The FROZEN line counts all frozen messages. For a breakdown by sender instead of recipient, use exiqsumm -s. Pair this with log analysis to correlate queue spikes with application errors or spam bursts.

Viewing Individual Message Details

Once you identify a suspect message ID, pull its headers with exim -Mvh <msgid>. You see the full envelope sender, recipients, and every header the message carried on arrival:

exim -Mvh 1sA3Bz-0002kL-7G

The output includes internal Exim headers like retry information and the freeze reason if applicable. Look for lines starting with X-Failed-Recipients: or -frozen tags. For the message body, use exim -Mvb <msgid>. This prints the raw MIME content, useful when you suspect the body triggered a content filter or contains malformed encoding.

The delivery log for a message lives in exim -Mvl <msgid>. It shows every delivery attempt, temporary failures, and retry schedule updates. A frozen bounce message log might show:

2025-01-10 14:22:03 1sA1Xy-0001pQ-2F <= <> R=1sA0Zw-0001mN-4A U=mailnull P=local S=1234
2025-01-10 14:22:05 1sA1Xy-0001pQ-2F ** bounce@example.com: retry timeout exceeded
2025-01-10 14:22:05 1sA1Xy-0001pQ-2F Frozen (delivery error message)

The double asterisk marks a permanent failure. The freeze reason appears on the last line. When diagnosing, grep /var/log/exim/mainlog for the message ID to see the full timeline, including SMTP conversations and router decisions. The -Mv flags give you the current state; the main log shows how you got there. To add a recipient to an existing queued message, use exim -Mar <msgid> new-recipient@domain.com, though this is rare outside testing.

Understanding Frozen Messages

Exim freezes a message when it cannot deliver or bounce it safely. A frozen message stays in the queue until you intervene; it will not retry automatically. Common triggers include repeated temporary failures that exhaust the retry rules, malformed envelope addresses, and bounce loops where a delivery failure notification itself fails.

You see the freeze reason in exim -bp output after the *** frozen *** marker. Real examples:

*** frozen *** (sender verify failed)
*** frozen *** (delivery error message)
*** frozen *** (message too old)

The first means the sender address failed verification: the sending domain does not accept mail to that address, or DNS lookups failed. The second is a bounce loop: the original message bounced, Exim generated a delivery failure notice, and that notice also failed. The third appears when a message sits in the queue beyond the timeout threshold (default 7 days) without successful delivery. For detailed guidance on configuring sender verification and retry rules, see the Exim specification.

Common Freeze Reasons

Sender verify failures happen when your Exim is configured to call back the envelope sender's domain. If that domain is unreachable or rejects the probe, Exim freezes the message. DKIM or SPF failures on bounce messages also cause freezes: the bounce has an empty envelope sender, and some configurations reject it. Malformed From: headers, especially those with unquoted special characters or missing angle brackets, trigger syntax errors that lead to freezes. Check exim -Mvh output for the exact parse error.

Reading the Freeze Tag in the Queue

The one-line freeze tag in exim -bp is abbreviated. For the full reason, run exim -Mvh <msgid> and look for the -frozen line near the top. It often includes the last SMTP error or the router that rejected the message. A message frozen due to retry timeout shows the final temporary error before Exim gave up. Messages frozen as delivery error notifications are almost always bounce loops; check the original message ID in the headers to trace the chain. Frozen messages do not auto-thaw. You must manually force delivery with exim -M, thaw with exim -Mt, or delete with exim -Mrm.

Forcing Delivery and Retries

When an SMTP server comes back online or you fix a DNS issue, messages sit in the queue waiting for the next scheduled retry. Force an immediate delivery attempt with exim -M <msgid>. This bypasses the retry schedule and runs all routers and transports for that message right now:

exim -M 1sA3Bz-0002kL-7G

You see delivery progress in real time on the terminal. If the message succeeds, it leaves the queue. If it fails, Exim updates the retry hints and requeues it. To retry all messages destined for a specific domain, use exim -R <domain>. This is the tool for targeted recovery after a remote server outage:

exim -R domain.com

Every queued message with a recipient at domain.com gets a delivery attempt. For a queue with thousands of messages to that domain, this runs in the background; check /var/log/exim/mainlog for results. The exim -qff command forces delivery for the entire queue, ignoring retry rules. Use it sparingly: it hammers every remote MX simultaneously and can trigger rate limits or get your IP flagged. Prefer -R when you know the problem domain. If a message is undeliverable and you want to bounce it immediately instead of waiting for the timeout, run exim -Mg <msgid>. This generates the delivery failure notice and removes the original from the queue.

Deleting and Freezing Messages

Delete a message from the queue with exim -Mrm <msgid>. The message and its spool files disappear immediately, no bounce sent:

exim -Mrm 1sA1Xy-0001pQ-2F

To freeze a message manually, stopping all delivery attempts without deleting it, use exim -Mf <msgid>. This is useful when you suspect a message will trigger a remote abuse complaint or content filter but want to inspect it before deciding. For bulk operations, pipe message IDs from exiqgrep to xargs exim -Mrm. The exiqgrep tool filters the queue by sender, recipient, age, or freeze status. To delete all frozen messages older than 7 days (604800 seconds):

exiqgrep -z -o 604800 | xargs exim -Mrm

The -z flag selects frozen messages, -o matches those older than the given age. Other useful flags: -f <sender> for envelope sender, -r <recipient> for any recipient, -y <seconds> for messages younger than the threshold. Combine them to narrow the filter: exiqgrep -f spam@example.com -o 86400 finds all messages from that sender older than one day. Always run exiqgrep alone first to preview the IDs before piping to xargs.

Removing Spam or Backscatter

A spam burst often leaves thousands of frozen bounce messages. Identify the pattern: spammers forge your domain as the envelope sender, remote servers accept then bounce, and your Exim queues the undeliverable bounces. Filter by sender address (the empty sender <> for bounces) or by a substring in the recipient. To remove all bounces:

exiqgrep -f '<>' -z | xargs exim -Mrm

If spam came from a specific compromised account, delete by sender:

exiqgrep -f compromised@yourdomain.com | xargs exim -Mrm

Check exiqgrep output for legitimate mail before running the delete. A backscatter flood can freeze tens of thousands of messages in hours.

Bulk Queue Operations

For operations on hundreds or thousands of messages, run exiqgrep output through a shell loop instead of xargs to add logging or conditional logic. This script deletes frozen messages but logs each ID:

exiqgrep -z | while read msgid; do
  echo "Removing $msgid" >> /var/log/exim/manual-purge.log
  exim -Mrm "$msgid"
done

You can also redirect exiqgrep output to a file, review it, then feed it to xargs. Never use shell globs or rm directly on /var/spool/exim/input; Exim maintains internal indexes that break if spool files vanish without the daemon knowing.

Clearing Queue Backlogs Safely

A queue with 50,000 or more messages slows every Exim operation. Running exim -bp can take minutes and exhaust memory. Use exim -bpc to get just the count; it reads the queue index without formatting output:

exim -bpc

You see a single number. If the count is high, split your cleanup into phases: freeze everything to stop new delivery attempts, audit the frozen set to identify spam, delete the junk, then thaw and retry legitimate mail. The workflow looks like this:

exim -qff -Mf  # Force queue run, freeze all messages
exiqgrep -z | head -100 | while read msgid; do exim -Mvh "$msgid" | less; done  # Sample frozen headers
exiqgrep -z -f '<>' | xargs exim -Mrm  # Delete backscatter
exiqgrep -z | xargs exim -Mt  # Thaw remaining
exim -q  # Normal queue run

The -Mt flag thaws a frozen message, allowing normal retry rules to apply. Set queue_run_max in exim.conf to limit parallel delivery processes during catch-up. The default is 5; raise it to 20 or 30 if your server has spare CPU and network capacity, but watch for rate limits on remote MXs. For comprehensive mail server tuning, see Linux Mail Server Configuration.

When the Queue Exceeds System Limits

Large queues consume inodes. Each message uses at least two files in /var/spool/exim/input: one for headers, one for body. A queue of 100,000 messages can exhaust a filesystem with a low inode count. Check with df -i. If inodes are near capacity, Exim cannot accept new mail. Delete spam and frozen messages first to free inodes before attempting legitimate retries. On systems with millions of messages, consider splitting the spool across multiple filesystems or using a filesystem with dynamic inode allocation like XFS. Never let the queue grow unbounded; set queue_only_load and smtp_accept_queue_per_connection to throttle intake when the system is overloaded.

Splitting Queue Runs by Priority

Process frozen messages separately from active ones. Frozen messages will not retry on their own, so a normal exim -q ignores them. Run exim -qf to include frozen messages in the queue run, but only after you have deleted obvious spam. For a large backlog, process high-priority domains first with exim -R, then run a full queue pass. You can also use queue_smtp_domains in your configuration to hold mail for specific domains in the queue without attempting delivery, useful when a partner's MX is down for maintenance and you want to avoid filling logs with temporary failures. Thaw those messages with exim -R <domain> once the remote server is back.

Common Exim Queue Problems and Fixes

Messages stuck in the queue usually fall into a few patterns, each with a signature in /var/log/exim/mainlog. Recognizing the log entry tells you whether the problem is local configuration, remote server behavior, or DNS. Exim's retry schedule controls how often it attempts redelivery after a temporary failure; the default in exim.conf looks like this:

begin retry
*                      *           F,2h,15m; G,16h,1h,1.5; F,4d,6h

This rule says: for any address and any error, retry every 15 minutes for 2 hours, then every hour (with exponential backoff factor 1.5) for 16 hours, then every 6 hours for 4 days. After 4 days of temporary failures, Exim gives up and bounces. If a remote server responds with 4xx codes in a loop (often due to greylisting or rate limiting), check your SPF record and reverse DNS. Many servers reject or defer mail from IPs without valid PTR records. Run dig -x <your-ip> and verify the result matches your HELO hostname.

Messages Stuck in Retry Loop

A mainlog entry for a retry loop looks like this:

2025-01-15 10:42:17 1sB7Qx-0003aR-5K == user@domain.com R=dnslookup T=remote_smtp defer (-44): SMTP error from remote mail server after RCPT TO:<user@domain.com>: 451 4.7.1 Greylisting in action, please come back later

The defer (-44) is a temporary failure. Exim schedules the next retry according to your rules. If the remote server keeps responding with 4xx, the message stays queued until the retry timeout. You can force an immediate retry with exim -M <msgid>, but that rarely helps; the remote server will greylist again. Instead, wait for the scheduled retry or check whether your IP is on a blocklist. If you see 421 4.7.0 Try again later or similar rate-limit messages, slow your delivery with smtp_accept_queue_per_connection or add a delay in the transport. For persistent 4xx loops from a single domain, contact their postmaster; sometimes your IP needs manual whitelisting.

Mailbox Full or DNS Lookup Failures

A mailbox-full error appears as a permanent failure:

2025-01-15 11:03:22 1sB8Fy-0004cL-2P ** recipient@domain.com R=dnslookup T=remote_smtp: SMTP error from remote mail server after RCPT TO:<recipient@domain.com>: 552 5.2.2 Mailbox full

The double asterisk marks permanent failure. Exim will retry a few times in case the user clears space, but eventually it bounces. To force an immediate bounce and clear the queue, run exim -Mg <msgid>. DNS lookup failures show up differently:

2025-01-15 11:15:08 1sB9Az-0005kN-7R == user@example.com R=dnslookup defer (-1): host lookup did not complete

This means the MX or A record query timed out or returned SERVFAIL. Verify with dig MX example.com and check /etc/resolv.conf for valid nameservers. If your local resolver is broken, every outbound message will defer. In exim.conf, the dnslookup router uses the system resolver by default; you can override with dns_lookup_order or point to specific nameservers in a custom resolver configuration. Frozen messages due to sender verification show:

*** frozen *** (sender verify failed)

This happens when require verify = sender is enabled and the envelope sender's domain does not accept callbacks. If you are freezing legitimate mail from domains with strict anti-spam policies, whitelist those senders in your ACL or disable sender callouts for known partners. Always test sender verification rules in warn mode before switching to deny.

Monitoring Queue Health in Production

Track queue size continuously to catch backlogs before they become emergencies. A cron job running exim -bpc every 5 minutes and alerting when the count exceeds a threshold is the simplest approach:

#!/bin/bash
COUNT=$(exim -bpc)
if [ "$COUNT" -gt 500 ]; then
  echo "Exim queue: $COUNT messages" | mail -s "Queue alert" admin@yourdomain.com
fi

Store the count in a time-series database for graphing. The exim_exiqsumm output works well in monitoring dashboards; parse it with awk or a log collector to extract per-domain counts. Nagios and Icinga have check_mailq plugins that understand Exim's queue format; configure thresholds for warning and critical states. For centralized logging, ship /var/log/exim/mainlog to an aggregator and create alerts on patterns like frozen, failed, or retry timeout exceeded. Correlate queue spikes with application deployment times or traffic surges.

The exiwhat command shows what each Exim process is doing right now:

exiwhat

You see output like:

1234 daemon: -q30m, listening on port 25
1235 running queue: 1sB7Qx-0003aR-5K from <sender@example.com>
1236 delivering 1sB8Fy-0004cL-2P to remote_smtp: user@domain.com

This is invaluable during a delivery storm when queue runs stack up and you need to know whether Exim is stuck or just slow. If you see dozens of processes all delivering to the same domain, that domain is likely rate-limiting you or has a slow MX. For deeper insight into service health and log correlation, see Linux Log Management & Troubleshooting. Use exiwhat output in your monitoring to detect hung delivery processes; if a process stays in the same state for more than a few minutes, it may be waiting on a network timeout or a remote server that accepted the connection but never responds.

Exim Queue Maintenance Checklist

Run exim -bpc daily and investigate any count above your baseline. Review frozen messages weekly with exiqgrep -z and delete obvious spam or backscatter. After any configuration change to ACLs, routers, or transports, force a test message through with exim -bt to verify routing, then check the queue for unexpected freezes. Set up automated alerts for queue size, frozen message count, and delivery failure spikes. Keep /var/spool/exim on a filesystem with ample inodes and monitor disk usage with df -i. Document your retry rules and test them: send a message to a domain with a deliberately unreachable MX and watch the retry schedule in the logs.

When clearing a backlog, always sample frozen messages with exim -Mvh before bulk deletion to avoid losing legitimate mail. Use exiqgrep filters to narrow deletions by sender, recipient, or age. After a major incident, audit your sender verification and SPF configuration to prevent recurrence. For a complete reference on building and maintaining mail infrastructure, see Linux Mail Server Configuration. Keep your Exim version current; security patches and bug fixes often address queue-handling edge cases. Test queue operations in a staging environment before running them in production, especially bulk thaws or forced retries that can generate traffic bursts.

Frequently Asked Questions

What does a frozen message in Exim mean?

A frozen message is one Exim has stopped trying to deliver. It stays in the queue until you manually intervene with exim -M, exim -Mt, or exim -Mrm. Common causes include repeated temporary failures that exceed retry limits, bounce loops, sender verification failures, and malformed headers. Check the freeze reason with exim -Mvh <msgid> and look for the -frozen tag near the top of the output.

How do I delete all frozen messages in Exim?

Use exiqgrep -z | xargs exim -Mrm. The -z flag selects only frozen messages, and xargs passes each message ID to exim -Mrm for deletion. Always run exiqgrep -z alone first to preview the list. For frozen messages older than a specific age, add -o <seconds>: exiqgrep -z -o 604800 | xargs exim -Mrm deletes frozen messages older than 7 days.

How do I force Exim to retry delivering a message immediately?

Run exim -M <msgid> to bypass the retry schedule and attempt delivery now. For all messages to a specific domain, use exim -R <domain>. This is useful after fixing a DNS issue or when a remote server comes back online. If the message is frozen, thaw it first with exim -Mt <msgid>, then force delivery. The -qff flag forces a full queue run ignoring retry rules, but use it carefully to avoid hammering remote servers.

Why is my Exim queue stuck with thousands of messages?

Common causes: a remote MX is down or rate-limiting you, DNS resolution is failing, your IP lacks reverse DNS or is blocklisted, or a spam burst filled the queue with backscatter. Run exim -bp | exiqsumm to see which domains dominate. Check /var/log/exim/mainlog for patterns like defer or frozen. Verify DNS with dig, check your SPF and PTR records, and use exiqgrep to isolate and remove spam before retrying legitimate mail.

How do I check the Exim mail queue size without listing all messages?

Run exim -bpc to print only the count. This reads the queue index and returns a single number, even for queues with hundreds of thousands of messages. It is fast and safe to run frequently in monitoring scripts. For more detail without full output, use exim -bp | exiqsumm to get per-domain counts and total volume. The mailq command is an alias for exim -bp and shows the full listing.

Share this article:
Nico Brandt
About the Author

Nico Brandt

JavaScript Development, TypeScript Engineering, Web Application Architecture, Technical Documentation

Nico Brandt is a JavaScript and TypeScript developer focused on building well-structured, maintainable, and scalable web applications.

He works extensively with modern JavaScript and TypeScript across frontend and backend environments, emphasizing type safety, code readability, and predictable application behavior.

...
JavaScript TypeScript Frontend Development Backend APIs Asynchronous Programming

Stay Updated

Subscribe to our newsletter for the latest tutorials, tips, and exclusive offers.