Skip to content

Outgoing mail is deferring

When to use: the Mail queue keeps growing, mailctl doctor reports a large mail queue count, users say their mail “is stuck in the outbox” or recipients never receive it, or the Delivery log shows outbound mail as soft (deferred). Preconditions: shell access to the host; admin access to the console.

A deferred message is not lost: Postfix retries it on a backoff (from 5 minutes up to a little over an hour between attempts) and returns it to the sender only after 5 days in the queue. Find the cause before doing anything to the queue — the cause decides whether a flush helps or hurts.

Not deferral: refused at sending with “Sending is suspended”. If users are refused when they send, with 554 5.7.1 … Sending is suspended: this server's licence has lapsed, nothing is queued and nothing is wrong with delivery: the licence lapsed on billing more than 90 days ago (the send_suspended stage). Mail to addresses on this server still goes through. Renew on Licence, or mailctl license activate; sending returns at once (Licensing).

  1. See what is waiting and why. Mail queue in the console lists every message with Postfix’s own delay reason (Mail queue (viewer, hold / release / delete)). On the host, the same queue grouped by reason:

    Terminal window
    docker exec mailserver-postfix-1 postqueue -p | tail -1
    docker exec mailserver-postfix-1 postqueue -p | grep '^ *(' | sort | uniq -c | sort -rn | head

    The first prints the total (-- 60 Kbytes in 19 Requests.); the second counts each distinct reason. The delivery attempts themselves are in the Postfix log and, classified, in the Delivery log (direction outbound):

    Terminal window
    docker exec mailserver-postfix-1 grep 'status=deferred' /var/log/mail/postfix.log | tail -20

    Each line names the relay= Postfix tried and the remote server’s reply. One reason across every destination points at this server (steps 2–4); one destination with many deferrals points at that receiver (step 5); one sender with many messages points at a mailbox (step 6).

  2. connect to …:25: Connection timed out for every destination: outbound port 25 is blocked. Most cloud providers block it by default. mailctl doctor tests it directly (outbound smtp :25). AWS, Oracle and Hetzner lift the block on request; GCP and Azure never do. The alternative is an outbound relay: Settings → Outbound relay. See Doctor → Common findings.

  3. A relay is configured and every message defers at it. With a relay, every outbound line names it as relay=, and the reply is the relay’s. Check what Postfix is using:

    Terminal window
    docker exec mailserver-postfix-1 postconf -h relayhost
    docker exec mailserver-postfix-1 grep 'SASL authentication failed' /var/log/mail/postfix.log | tail -10
    • SASL authentication failed with a reply from the relay — the relay refused the username and password. The usual cause is a credential rotated or revoked at the relay provider (for Amazon SES, the SMTP credentials of an IAM user). Generate new ones there and enter them on Settings → Outbound relay, then Validate & apply.
    • SASL authentication failed … no mechanism available — the postfix image lacks the SASL modules; see Doctor.
    • The relay password cannot be read back — it is stored sealed and is write-only: no screen, API response or command shows it. If Settings → Outbound relay says The stored password can no longer be read, SECRET_ENCRYPTION_KEY has changed since it was saved (config.relay.passwordUnreadable in GET /api/v1/server/config). That alone does not stop mail — Postfix keeps the credentials it already has for that host and username — but enter the password again before the next change to the relay (Backup & restore → SECRET_ENCRYPTION_KEY is part of the backup).
    • With a relay configured Postfix requires TLS to it (smtp_tls_security_level = encrypt), so a relay host or port that does not offer STARTTLS defers every message too.

    mailctl doctor reports the configured relay as an outbound relay line but does not test it, so a relay that refuses the credentials looks healthy there.

  4. Host or domain name not found for every destination — the server cannot resolve DNS. For one domain only, that domain’s MX is broken and the problem is the recipient’s.

  5. One receiver defers or refuses: reputation and rate limits.

    • The reply names a blocklist (blocked using zen.spamhaus.org, Spamhaus, SpamCop, Barracuda) or says “spam” or “reputation”: the Delivery log classes it reputation. Follow The server IP is on a blocklist, and check the dashboard’s Server reputation.
    • The receiver is rate-limiting this server (a 4xx naming rate, volume or “try again later”): the Delivery log classes it policy. It clears on its own as Postfix’s retries spread out. A new IP is the usual cause: keep IP warm-up on (Outbound rate limits) and see Mail from a domain lands in spam.
  6. One sender fills the queue: a compromised mailbox. Hundreds of messages from one address, to recipients that mean nothing to its owner, is the signature. Look for:

    • an outbound_abuse alert on the dashboard, which names the mailbox. By default it only alerts — the mailbox keeps sending until somebody acts (Compromised-mailbox containment);
    • a rate_limit_burst warning: the mailbox keeps hitting its own hourly limit.

    Stop it by suspending the mailbox (Suspended on its page, which also refuses its sign-ins and its inbound mail; its stored mail is kept), then follow Security incident. Then deal with what it already queued: search the Mail queue for the sender, Hold the messages (reversible; held mail is never delivered), and delete them once you no longer need them as evidence. Deleting is permanent and silent: the sender is not told, and nothing reaches the delivery log.

    The outbound rate limits are not what fills the queue. A sender over its hourly budget is refused at submission with 451 4.7.1 Ratelimit "user" exceeded (or "sender_domain", "warmup"), so that mail stays in the client’s outbox and never reaches the queue. The Delivery log shows those refusals as Rate limited.

  7. Once the cause is fixed, let the queue drain. Postfix retries everything deferred on its own schedule, so doing nothing is always safe. To retry now:

    Terminal window
    docker exec mailserver-postfix-1 postqueue -i <queue id> # one message
    docker exec mailserver-postfix-1 postqueue -f # everything deferred

    The console deliberately has no flush; Release only returns a held message to deferred to wait for the next retry. Do not flush:

    • while the server is listed on a blocklist or a receiver is rate-limiting it — every early retry is another refusal, and flushing a queue into a blocklist is how a server gets listed more widely;
    • while the queue still holds a compromised mailbox’s mail — hold or delete that first, or the flush delivers the spam;
    • before the fix is confirmed — retry one message with postqueue -i and read its result in the log first.

postqueue -p (or the Mail queue tiles) shows the deferred count falling; the Postfix log shows status=sent for new outbound mail; a test message to an outside address (Gmail, say) arrives. With a relay, the status=sent lines name it as relay=.

Retrying, holding and releasing are reversible. Deleting a queued message is not: nothing on the server can bring it back. A relay you configured to work around a blocked port 25 is removed by clearing its host on Settings → Outbound relay (Leave the host blank to deliver directly) and applying; the previous configuration is also in GET /api/v1/server/config/history.