Skip to content

Diagnose a stuck or aging Postfix queue

When mail is not being delivered, the Postfix queue is the source of truth. This guide shows you how to inspect what is queued, how old it is, and why it is stuck.

incoming active delivered
deferred bounced after maximal_queue_lifetime (default 5 days)
hold held by an admin (postsuper -h); released manually
A message moves incoming to active, then delivers or fails. A transient failure parks it in the deferred queue, where Postfix retries on a backoff until it succeeds or hits the queue lifetime and bounces.

The fastest way to see what mail is stuck is postqueue -p, which is identical to mailq. This command lists every message in the queue with its ID, size, arrival time, and the reason it cannot be delivered.

-Queue ID- --Size-- ----Arrival Time---- -Sender/Recipient-------
DBA3F1A9 553 Mon Jun 16 14:42:15 sender@internal.com
(connect to mail.example.com[192.168.155.63]: Connection refused)
recipient@example.com
C1AB5F2D 1247 Mon Jun 16 14:15:03 orders@shop.local
(host mail.shop-provider.net said: 451 Service unavailable)
customer@shop-provider.net
-- 2 Kbytes in 2 Requests.

Each message occupies one or more lines. The first line shows:

  • Queue ID: The file name used internally (e.g. DBA3F1A9). This ID is used with postcat and postsuper to inspect or manipulate the message.
  • Size: Message size in bytes.
  • Arrival Time: When the message arrived in the queue.
  • Sender/Recipient: The sender email address on the first line, followed by recipient addresses on subsequent lines.

For deferred messages (those that failed delivery and are being retried), the line after the sender shows the reason for the most recent failure in parentheses. This is the core diagnostic: it tells you exactly why delivery failed.

The summary line at the end shows the total queue size in Kbytes and the number of messages (requests).

Spot which domain is backing up with qshape

Section titled “Spot which domain is backing up with qshape”

When your queue grows, postqueue -p tells you why each message failed, but it does not show you which domain is the culprit or how the problem spreads over time. This is where qshape shines: it reorganizes the queue by destination domain and groups messages by age, making patterns instantly visible.

To find which destination is responsible for a large queue, use qshape deferred to analyze aged messages waiting for retry, or qshape active to see which domains Postfix is actively trying to deliver to right now.

qshape deferred

Output:

T 5 10 20 40 80 160 320 640 1280 1280+
486 yahoo.com 0 0 1 0 0 2 4 20 40 419
14 extremepricecuts.net 0 0 1 0 0 0 0 1 0 12

The output is organized by domain (left column) with age buckets as numeric columns. The T column shows the row total (sum of all messages for that domain).

  • T: Row total. The sum of messages in all age buckets for that domain. In the first example, yahoo.com has 486 total messages in the deferred queue.
  • Age buckets (5, 10, 20, 40, 80, 160, 320, 640, 1280, 1280+): Each column represents a time range in minutes. The first bucket (5) counts messages 0-5 minutes old, the next (10) counts 5-10 minutes old, and so on. Columns double in age each step. The final column (1280+) captures messages 1280 minutes or older (about 21+ hours). This doubling pattern makes it easy to spot whether a domain is recovering (messages flowing left toward recent buckets) or stuck (accumulating in the rightmost columns).
  • Rows: Each domain shows its message count distribution across age buckets, revealing which destinations are slow versus which are completely blocked.

In the example above, yahoo.com has 419 messages that have been waiting longer than 1280 minutes (21+ hours), while extremepricecuts.net has 12 such messages. The rightmost column is the smoking gun: these domains have very old mail stuck in the queue, indicating a serious delivery problem that has persisted for hours. This is far faster than scanning postqueue -p output by hand, especially on large systems with thousands of messages.

Postfix maintains five separate directories under its spool (usually /var/spool/postfix) for different message states. Knowing which queues exist and what they mean helps you understand where messages live when things go wrong.

Incoming: Messages that arrived from the network or from local programs. Postfix routes these into the active queue very quickly, usually within a second or two. Buildup here is rare and signals a processing problem.

Active: Messages that the queue manager is currently attempting to deliver. Only a limited number of messages can be in this queue at once to prevent overwhelming the system (controlled by default_process_limit). This is the “leaky bucket” that feeds delivery attempts. When active is full, new messages wait in incoming.

Deferred: Messages that failed at least one delivery attempt and are waiting for the next retry. Messages stay here until they are successfully delivered or until they expire after maximal_queue_lifetime (default 5 days). This queue grows when destinations are slow or unreachable. Use qshape deferred to see how old the messages are and which domain is stuck.

Hold: Messages that have been administratively placed on hold (via postsuper -h). They sit here inert until explicitly released with postsuper -H. This queue is used to temporarily isolate or quarantine messages while investigating.

Corrupt: Queue files that are unreadable or damaged. These require investigation and usually manual deletion or analysis with postcat.

Most queue backlog appears in deferred and active. Use qshape deferred and qshape active to see the size and age distribution of each. High numbers in deferred signal delivery problems at remote hosts; high active queue size signals local processing constraints.

To read the full content of a queued message, use postcat -q <queueid>:

postcat -q DBA3F1A9

This displays the message envelope (sender, recipients, Postfix metadata), headers, and body. Use this when you need to understand what a queued message contains or verify that a bounce/rejection reason is legitimate.

When a delivery attempt fails, Postfix does not immediately try again. Instead, it uses exponential backoff to avoid hammering a remote server that is down or slow. This is polite behavior and conserves bandwidth, but it also means queued mail does not move as fast as you might want. Understanding the retry schedule helps you know whether a stuck queue will resolve itself or is a true problem.

Four parameters control how Postfix retries deferred mail and when it gives up.

queue_run_delay (default 300 seconds): How often the queue manager scans the deferred queue to find messages that are eligible to retry. With a 300-second scan interval, the queue manager checks for retryable mail every 5 minutes. This is the heartbeat of the retry engine.

minimal_backoff_time (default 300 seconds): The shortest time Postfix will wait after a failure before attempting delivery again for the same message. Even if the destination comes back online immediately, Postfix waits at least this long before trying again to avoid thundering-herd behavior.

maximal_backoff_time (default 4000 seconds, about 67 minutes): The longest time Postfix will wait between attempts. Postfix doubles the backoff time after each failure until it reaches this ceiling. After that, delivery attempts are spaced this far apart. For a message that fails repeatedly for hours, backoff climbs to 4000 seconds and stays there.

maximal_queue_lifetime (default 5 days): The absolute cutoff. If a message cannot be delivered within 5 days, Postfix bounces it (sends a non-delivery notice to the sender) and removes it from the queue. Very old mail sitting in your deferred queue is likely to be approaching or past this limit.

A message destined for example.com fails at 14:42 UTC. The delivery error is temporary (DNS timeout, connection refused, or 4xx code). Postfix records the failure and schedules the next attempt.

  • First retry (14:42 + 300s = 14:47): Fails again. Backoff is now 600 seconds (doubled).
  • Second retry (14:47 + 600s = 14:57): Fails. Backoff doubles to 1200 seconds (20 minutes).
  • Third retry (14:57 + 1200s = 15:17): Fails. Backoff doubles to 2400 seconds (40 minutes).
  • Fourth retry (15:17 + 2400s = 15:57): Fails. Backoff doubles to 4800 seconds, but hits the ceiling of 4000 seconds.
  • Fifth and subsequent retries: Spaced 4000 seconds apart until the message is 5 days old, at which point it is bounced.

If the destination comes online after 6 failed attempts, the next scheduled retry succeeds, the message is delivered, and the queue entry is removed. But if the destination stays down, the message waits in deferred until the 5-day limit is reached.

To force Postfix to attempt delivery on all deferred messages immediately:

postqueue -f

This attempts delivery of all queued messages, overriding the normal retry backoff schedule. Use this cautiously: if the destination is down or slow, this can cause sudden traffic spikes and potentially worsen congestion. Repeated flushing of a large deferred queue often makes matters worse, not better, because you are forcing Postfix to repeatedly hit a broken destination.

Hold a specific message:

postsuper -h <queueid>

Release all held messages:

postsuper -H ALL

(The ALL argument must be uppercase.) This is useful for testing before releasing mail in bulk or for separating problematic messages while you investigate.

Remove a message from the queue:

postsuper -d <queueid>

Do not use this lightly. The sender will never know the message failed. Use it only for messages you are certain should not be delivered (e.g., messages to test addresses, or duplicates).

A stuck queue often forms gradually, and spotting it early is hard with manual commands. Postfix Insights surfaces queue age and slow domains directly from your mail logs, so you see the problem before mail piles up.

When Postfix Insights shows elevated “oldest undelivered” metrics or a rising “queue age” chart, it means the tools above will reveal a domain backing up. Run qshape deferred immediately to confirm which destination is responsible, then use postqueue -p to see the specific errors.

For the full context on what deferred means and how it differs from bounced mail, see the guide Deferred vs. bounced Postfix mail.

To begin monitoring your mail flow, see Quick start.

For the full Postfix Insights source code and issue tracker, visit github.com/Xodus-CO/postfix-insights.

  • Postfix QSHAPE_README: Detailed explanation of queue shape analysis and bottleneck identification.
  • postqueue(1): Postfix queue control command.
  • postsuper(1): Postfix queue superintendent (hold, release, delete).
  • postcat(1): Display queue file contents.
  • qmgr(8): Queue manager and retry logic.
  • postconf(5): Configuration parameter reference, including backoff and lifetime settings.