The multi-IP rotation was built to spread risk while DKIM was broken (fixed
2026-06-17) and after the May 30-31 over-volume blast. With DKIM signing
correctly, spreading ~3k trucking msgs/day across 12 IPs (.94-.105) + ~1.2k
healthcare msgs/day across 3 IPs (.107-.109) gave each IP far too little
per-receiver volume to build reputation. Gmail/Outlook read it as snowshoe spam
and reputation-blocked ~200 msgs/day ("very low reputation of the sending
domain") -> 0 human clicks, 0 sales.
Consolidate to ONE IP per stream so each accrues real reputation:
- trucking: pw-mta-warmup ALL=(out05) -> randmap collapses to {out05:} = .94
- healthcare: listmonk-hc SMTP servers 2/3 (ports 2527/2528 -> .108/.109)
disabled in DB; all HC mail now egresses .107 (hcmta01). [applied live]
Applied live: transport_maps now randmap:{out05:}; listmonk-hc restarted.
To re-expand later: add transports back to ALL + re-enable the HC SMTP servers.
72 lines
2.8 KiB
Bash
Executable file
72 lines
2.8 KiB
Bash
Executable file
#!/bin/bash
|
|
# Postfix IP-warmup scheduler.
|
|
#
|
|
# Gradually expands the outbound sending-IP rotation pool over a warmup window,
|
|
# so we don't blast 20 brand-new IPs at consumer ISPs on day one (which looks
|
|
# like snowshoe spam and torches reputation).
|
|
#
|
|
# IP layout:
|
|
# .90 / mta01 -> dedicated Yahoo/AOL recovery IP (transport: yahooslow)
|
|
# .91-.109 / mta02-mta20 -> rotation pool (transports: out02..out20)
|
|
#
|
|
# transport_maps = hash:/etc/postfix/transport, randmap:{<active pool>}
|
|
# - hash routes yahoo/aol -> yahooslow (the dedicated .90 trickle)
|
|
# - randmap round-robins the source IP across the *active* rotation pool
|
|
#
|
|
# This script (run daily by cron) recomputes the active pool from the warmup
|
|
# start date and reloads Postfix only when the pool changes. It never shrinks.
|
|
#
|
|
# Run once with `--start` to stamp the start date.
|
|
set -euo pipefail
|
|
|
|
STATE=/etc/postfix/pw-warmup-start
|
|
MAINCF=/etc/postfix/main.cf
|
|
POSTCONF=/usr/sbin/postconf
|
|
POSTFIX=/usr/sbin/postfix
|
|
|
|
# Rotation transports. .90 is Yahoo-dedicated, not in rotation. out02/03/04
|
|
# (.91/.92/.93) were reputation-torched by the May 30-31 over-volume blast and
|
|
# are retired.
|
|
#
|
|
# CONSOLIDATED 2026-06-18: the multi-IP snowshoe rotation was built to spread
|
|
# risk while DKIM was broken (fixed 2026-06-17). With DKIM signing correctly,
|
|
# spreading ~3k msgs/day across 12 IPs gave each IP too little per-receiver
|
|
# volume to build reputation -> Gmail/Outlook flagged it as snowshoe spam
|
|
# ("very low reputation of the sending domain"). We now concentrate the entire
|
|
# trucking stream on ONE warm IP (out05 = .94) so it accrues real reputation.
|
|
# To re-expand later, add transports back to ALL and bump the schedule below.
|
|
ALL=(out05)
|
|
|
|
if [ "${1:-}" = "--start" ]; then
|
|
date +%s | sudo tee "$STATE" >/dev/null
|
|
echo "warmup start stamped: $(date)"
|
|
fi
|
|
[ -f "$STATE" ] || date +%s | sudo tee "$STATE" >/dev/null
|
|
|
|
START=$(cat "$STATE"); NOW=$(date +%s); DAYS=$(( (NOW - START) / 86400 ))
|
|
|
|
# Warmup schedule: day-since-start -> active rotation IPs
|
|
if [ "$DAYS" -le 3 ]; then N=3
|
|
elif [ "$DAYS" -le 7 ]; then N=5
|
|
elif [ "$DAYS" -le 11 ]; then N=8
|
|
elif [ "$DAYS" -le 17 ]; then N=12
|
|
elif [ "$DAYS" -le 24 ]; then N=16
|
|
else N=16; fi
|
|
|
|
# Safety: never request more transports than exist in ALL.
|
|
if [ "$N" -gt "${#ALL[@]}" ]; then N=${#ALL[@]}; fi
|
|
|
|
POOL=""
|
|
for ((i=0; i<N; i++)); do POOL="${POOL}${ALL[$i]}:,"; done
|
|
POOL="${POOL%,}"
|
|
NEWVAL="hash:/etc/postfix/transport, randmap:{${POOL}}"
|
|
|
|
CUR=$($POSTCONF -h transport_maps 2>/dev/null || echo "")
|
|
if [ "$CUR" != "$NEWVAL" ]; then
|
|
sudo $POSTCONF -e "transport_maps=${NEWVAL}"
|
|
sudo $POSTFIX reload >/dev/null 2>&1 || true
|
|
logger -t pw-warmup "day $DAYS -> $N rotation IPs active"
|
|
echo "$(date '+%F %T') warmup: day=$DAYS active_rotation_ips=$N"
|
|
else
|
|
echo "$(date '+%F %T') warmup: day=$DAYS active_rotation_ips=$N (no change)"
|
|
fi
|