Skip to content

What happens to a deferred message in Postfix

A deferred message in Postfix is not a terminal state. When the queue manager (qmgr) accepts a message and a delivery attempt returns a transient error (a 4.x.x enhanced status code in the sense of RFC 3463), Postfix does not give up: it re-queues the message and retries it later. Over the following hours or days, that single deferred message resolves into exactly one of four outcomes: it is delivered on a later retry, it stays deferred while retries continue, it expires because your server ran out of patience, or it bounces late because a retry hit a permanent error. Knowing which of the four actually happened, and why, is the difference between “the network blipped and healed itself” and “a message sat for five days and the sender only just found out it failed.”

Deferred
4.x.x transient
Delivered
a later retry succeeds
status=sent
Still deferred
in queue, retries continue
not final
Expired
queue lifetime exceeded, returned to sender
status=expired
Late bounce
a later attempt hits 5.x.x
status=bounced

A message becomes deferred when delivery fails for a reason that might succeed later. RFC 3463 classifies enhanced status codes by class: a 4.x.x code is a persistent transient failure, and a 5.x.x code is a permanent failure. On a transient (4.x.x) result, Postfix keeps responsibility for the message, places it in the deferred queue, and schedules a retry. This is the same contract RFC 5321 describes for SMTP: after accepting a message, a sender retries on transient conditions and only notifies the sender once it gives up or hits a permanent error.

For the difference between a deferral and a hard rejection at the front door, see rejected vs deferred vs bounced.

The qmgr scans the deferred queue on an interval set by queue_run_delay (default 300s). A message is not eligible on every scan, though. Each deferred message is given a cool-off time computed by doubling its age in the queue, then clamped to lie between minimal_backoff_time (default 300s) and maximal_backoff_time (default 4000s). Young messages retry more often; older ones back off. Optionally, delay_warning_time (default 0, which means disabled) sends the sender a “your message is delayed” notice while the message is still in flight. Postfix documents this backoff and scan behavior in QSHAPE_README and TUNING_README.

A later retry succeeds. The log shows one or more status=deferred lines for the queue ID, then a final status=sent:

Jun 17 04:11:02 mail postfix/smtp[2391]: A1B2C3D4E5: to=<ops@globex.co>, relay=mx.globex.co[198.51.100.20]:25, delay=5421, delays=0.04/0.01/0.2/5420, dsn=2.0.0, status=sent (250 2.0.0 OK)

Note the delay=5421: the message sat in the queue across retries for roughly an hour and a half before it left. The mail arrived, but the holding time is the story. Persistent large delays point to downstream flakiness, a slow or intermittently reachable next hop, or greylisting that eventually cleared.

The message has not resolved yet. It is in the active or deferred queue, and retries are continuing under the backoff schedule above. This is the only one of the four outcomes that is not final: every still-deferred message is on its way to one of the other three. If a large share of your queue is stuck here, see diagnose a stuck Postfix queue.

The message exceeded its maximum allowed time in the queue, so Postfix gave up and returned it to the sender, generating a non-delivery report (NDR). The qmgr logs the give-up with a distinctive shape:

Jun 17 09:40:55 mail postfix/qmgr[1180]: A1B2C3D4E5: from=<noreply@acme.io>, status=expired, returned to sender

The ceiling is maximal_queue_lifetime (default 5d) for normal mail and bounce_queue_lifetime (default 5d) for bounce and notification messages. This default aligns with RFC 5321, which says the give-up time generally needs to be at least 4 to 5 days. The key point: expiry is your server giving up because of elapsed time, not the remote server issuing a permanent rejection. Nobody said no. Your Postfix simply stopped trying.

On a later retry, an attempt returns a permanent error (a 5.x.x code), so the message bounces after having been deferred earlier. The log shows earlier status=deferred (4.x.x ...) lines, then a final status=bounced (5.x.x ...), and an NDR is generated for the sender. A transient condition turned permanent between attempts: greylisting that never cleared and eventually became a hard block, a mailbox that filled and was then deleted, or a policy that tightened.

Three of these four outcomes generate no NDR at the moment of deferral, so a quick glance at a single log line tells you nothing about how the story ended. The endings carry very different operational meaning:

  • Delivered eventually means downstream flakiness that self-healed. The mail got through, but watch your queue holding times, because rising delays are an early warning.
  • Expired means mail was held for days and then abandoned by your own server. This usually signals a persistent connectivity problem or a policy block at the destination, and the painful part is that the sender only learns of the failure days later.
  • Late bounce means a transient condition hardened into a permanent one. The destination changed its mind between attempts, and the message bounced after a delay rather than immediately.

All three tell a different story than a fast bounce, where the very first attempt got a 5.x.x and the sender knew within seconds. For the broader contrast between deferral and bouncing, see deferred vs bounced. To decode the enhanced status codes themselves, see the DSN status code reference.

Because a deferred message’s fate is written across multiple log lines, often spanning days, reconstructing it by hand means grepping for one queue ID and reading every status= entry in order. The first transient status=deferred tells you the message entered this lifecycle. The final line tells you which of the four outcomes it reached: status=sent (delivered eventually), no terminal line yet (still deferred), status=expired (your server gave up), or status=bounced with a 5.x.x (late bounce).

Postfix Insights correlates a message’s retries by queue ID, so instead of reconstructing the story from scattered status=deferred lines across days of logs, it shows what your deferred mail actually became. It charts the four outcomes, Delivered, Still deferred, Expired, and Late bounce, so you can see at a glance whether your deferrals are healing or quietly dying. It does not change how Postfix retries; it reads what Postfix already logged and groups it by the lifecycle described above.