diff --git a/docs/email-deliverability-runbook.md b/docs/email-deliverability-runbook.md index 4797dd2..33d248d 100644 --- a/docs/email-deliverability-runbook.md +++ b/docs/email-deliverability-runbook.md @@ -67,18 +67,29 @@ daily volume cap. Result: ## Fresh-IP warmup discipline (the rules) -1. **Small audiences.** Day 0-3: a few hundred TOTAL per day, not per campaign. - Lower the `limit` values in `build_trucking_campaigns.py` segment specs while - warming. -2. **Best recipients first.** Only verified / engaged addresses. Gmail and - Microsoft only (Yahoo family already excluded). -3. **Scrub hard bounces immediately.** `550 5.1.1` (no such user), full mailbox, - "not our customer" all hurt reputation signals. -4. **Watch the signals daily** (see commands below). If Gmail `550-5.7.1` or +The historical mail.log proves these IPs sustain ~2,500 sends/day at 68-76% +delivery once warm (May 19-21). Collapses only ever came from 17k-29k spikes. +So we ramp ASSERTIVELY but never spike. The Listmonk sliding-window cap +(`/usr/local/bin/pw-listmonk-rampcap`, daily cron 07:20 UTC, driven off the same +`/etc/postfix/pw-warmup-start` stamp) enforces this automatically: + +| warmup day | hourly cap | ~daily total | +|-----------:|-----------:|-------------:| +| 0-1 | 50/h | ~500 | +| 2-3 | 150/h | ~1,500 | +| 4-6 | 250/h | ~2,500 | +| 7+ | 300/h | ~3,000 (hard ceiling) | + +Hard rule from the data: **never exceed ~4k/day, never spike.** + +Other rules: +1. **Best recipients first.** Gmail + Microsoft + clean ISPs only (Yahoo family + already excluded). Send small focused batches, e.g. + `build_trucking_campaigns --only-segment mcs150 --max-per-segment 100 --date --send-hour `. +2. **Scrub hard bounces immediately.** `550 5.1.1`, full mailbox, "not our + customer" all hurt reputation signals. +3. **Watch the signals daily** (see commands below). If Gmail `550-5.7.1` or Yahoo `421 TSS04` reappear, STOP and hold for several days. -5. **Ramp Listmonk's sliding window in step with the IP warmup** (e.g. 50/h -> - 150/h -> 300/h as days pass and signals stay clean). Restart the listmonk - container after changing `settings`. ## Monitoring commands