Your internal SMTP server refused connections for forty minutes — expired certificate, exceeded quota, or freshly listed IP. Outage fixed. Admin clicks "retry all 12,000 failed emails". One hour later, Gmail throttles your domain, logs show 452 and 550, and real transactional mail (password reset) bounces.
Naive retry recreates the outage at larger scale.
Why blast retry is dangerous
| Risk | Mechanism | Consequence |
|---|---|---|
| ESP/ISP rate limit | Too many connections/min | 421, 452 temp errors cascading |
| Blacklist | Suspicious spike from your IP | All sends impacted |
| Local SMTP queue | Postfix/qmail saturated | Application timeouts |
| UX | Non-deduplicated duplicates | 3 identical invoices |
A failed email is not data to dump — it is a message with a cause, priority and validity window.
Classify before retrying
Hard bounce (5xx) — nonexistent address, policy reject: do not retry. Mark failed, clean database.
Soft bounce (4xx) — quota, greylisting, temp unavailable: retry with backoff.
Application timeout — your code got no response: investigate separately from SMTP bounce.
Store SMTP code, DSN and attempt count per message.
Exponential backoff and rate limit
Typical pattern:
| Attempt | Delay | Action |
|---|---|---|
| 1 | 5 min | Auto retry |
| 2 | 30 min | Auto retry |
| 3 | 2 h | Retry + ops alert |
| 4 | 24 h | Last attempt |
| 5 | — | Definitive failed |
Cap global throughput: e.g. 100 emails/minute max in recovery, even if your ESP allows more.
Implementation: Sidekiq retry, Laravel queue backoff, Celery retry_backoff, or cron reading outbound_mail table with next_retry_at.
Prioritise critical emails
Not all messages deserve the same SLA:
- P0 — password reset, 2FA, security alerts.
- P1 — order confirmations, invoices.
- P2 — newsletters, deferred marketing.
In recovery, process P0 first with a separate channel if possible (dedicated transactional ESP).
ESP vs self-hosted SMTP on hosting
On shared hosting or VPS, sending directly from Postfix exposes shared IP. Transactional ESPs absorb retry spikes and handle SPF/DKIM/DMARC.
If self-hosting: monitor postmaster tools, rDNS, and limit volume from a dedicated IP.
The peak: retry is a reputation test, not persistence
Measure bounces after retry, not just sends "accepted" by your SMTP.
Decide and move forward without blind spots
Persist status, error code and attempt count per message before you need recovery. Implement backoff and rate limit upstream — not on outage day. Separate transactional and marketing via domains or distinct ESPs. Test outage + recovery scenario in staging with realistic volume. Compare hosts and email limits in our directory.
Frequently asked questions
Should you retry immediately after an SMTP outage?
No. Wait until the service is stable, identify the cause (auth, quota, blacklist), then retry in batches with backoff — not a dump of thousands of simultaneous messages.
What is the difference between SMTP 4xx and 5xx errors?
4xx = temporary (greylisting, quota) → retry with backoff. 5xx = permanent (invalid address, policy) → do not insist, mark failed and alert.
How do you avoid blacklist during a large retry?
Limit throughput (messages/minute), spread over several hours, use a transactional ESP (Brevo, Mailgun, Postmark) with managed reputation, and monitor bounce rates.
Home-grown queue or managed service?
Home queue (Redis, database) if modest volume and ops team. ESP + bounce/complaint webhooks once volume or deliverability becomes critical.
After an SMTP outage, the question is not "how many to retry" — it is at what pace your reputation can absorb catch-up.
