Submission and delivery

Queues, retries, and delivery status

Explain store-and-forward delivery, temporary deferrals, permanent failures, and delivery-status notifications.

SMTP is store-and-forward. A server accepts a message, writes it to a queue, and tries the next hop when it can. Nothing has to be online at the same time.

That’s why my provider can say 250 to my app even when Sara’s server is down. It took responsibility. Delivering is now its problem.

What happens on each reply

A 4xx reply, or no reply at all, keeps the message in the queue. The server retries with growing delays: a minute, then five, then fifteen, and so on.

A 5xx reply ends delivery for that recipient. No more retries.

If delivery eventually fails after the original submission succeeded, the server creates a delivery status notification, the message most people call a bounce. It goes to the envelope return path from MAIL FROM.

The bounce itself uses an empty return path, <>. If the bounce also fails, nobody generates a bounce for the bounce. That rule is what stops infinite loops.

What a queue actually stores

A queue record is more than the message bytes. It tracks every recipient separately, because they can end differently:

[email protected]    delivered  250 2.0.0
[email protected]    deferred   451 4.4.1, retry at 14:30
[email protected]   failed     550 5.1.1

Sara is done. The team alias gets another attempt at 14:30. Marco failed permanently and will show up in a bounce.

On a Postfix server you can see this with mailq (or postqueue -p). Each entry shows the queue ID, the sender, and the reason the last attempt was deferred.

Back off, don’t hammer

Retries must spread out. A tight loop against a struggling server makes their incident worse and gets your IP rate-limited or blocked.

Eventually the message hits the queue lifetime, often around five days. The server gives up, removes it, and sends the bounce for every recipient still undelivered.

Bounce to the envelope, never to the header

Be careful with this one. A bounce goes to the reverse path, not to the visible From field. Those are often different addresses on purpose, as we saw with [email protected].

Software that bounces to From creates backscatter: spam with a forged From gets rejected, and an innocent person receives the bounce.

Queued is not delivered

When a sending API returns 200 OK, it usually means “we put it in the queue”. The message could still bounce hours later.

If your app shows email status to users, keep those states apart: submitted, delivered, bounced. Fill them in from delivery events as they arrive, not from the initial API response.

Try building a queue table for three recipients with one success, one temporary failure, and one permanent failure. Write the next action for each row.

Lesson completed