Outbound shaping and IP warm-up: one slow destination must not stall the queue
-
Since 6.3.4 the delivery queue shapes what it sends. It pauses a destination that asked it to slow down, caps how many sessions it holds open to one destination and how many deliveries one tenant has in flight, and holds a new outbound address to a daily ceiling at each large provider. Everything in this topic is new in 6.3.4 except
MaxOutboundPerDestinationPerMinute, a fixed per-destination rate that 6.3.3 already has. What to know first: of what this topic covers, two things change behaviour on upgrade without a switch. The back-off is on by default, and a deferral this server chose does not spend a message's retries until the message has been queued for longer than its retry schedule spans; only a failure at a remote does. The second covers every hold in this topic, and so changes an upgraded server that already setsMaxOutboundPerDestinationPerMinute. The connection ceilings, the tenant share and the warm-up do nothing until you set them.The order of the checks
For each batch of recipients on one target, the server judges in this order, before any connection is opened:
- The remote domain policy's largest message:
5.3.4, permanent, measured on the queued message. - The destination's back-off window. Held until the window ends; then one delivery goes through as the probe, and any other arriving meanwhile waits a minute.
- The policy's messages a minute (
max_messages_per_minute). - The connection ceiling: the policy's
max_connectionswhere the policy sets one, otherwise the server-wideMaxOutboundConnectionsPerDestination. - The tenant share,
MaxOutboundConnectionsPerTenant. - The server-wide
MaxOutboundPerDestinationPerMinute. - DNS: the MX lookup (or the route's host) and MTA-STS; then the IP warm-up's daily ceiling; then, where DANE applies, whether the MX set is DNSSEC-validated.
- Gateway encryption, for a domain whose policy encrypts. The copy is made here, so a batch that is only going to be deferred is never encrypted.
- The connection attempts.
Every one of 2 to 6 is a deferral with
4.4.5("mail system congestion") and a sentence naming the rule. The target each check is keyed by is what the batch is delivered to: the recipient domain of an MX delivery, or the host of a route or a relay, or its address when the route names an IP literal, lower-cased.The back-off: a destination said slow down
When a destination answers a delivery with a request to slow down, it gets a back-off window: nothing is sent to it until the window ends, one delivery then probes it, and the same answer doubles the window. Deliveries to every other destination are untouched.
What is read as a signal.
Reply Read as Code 421, unless an exclusion below applies A signal, whatever the text, at the banner or anywhere after it A 4xx whose enhanced status is 4.7.0,4.7.28,4.7.500,4.7.650,4.7.652or4.3.2A signal A 4xx whose text, lower-cased, contains one of the phrases below A signal The phrases:
rate limit,rate-limit,ratelimit,too many messages,too many connections,too much,unusual rate,unexpected volume,temporarily deferred,temporarily rate,throttl,try again later,server busy,back off,slow down. They are English, and a remote that says the same in another language is read by its codes alone. The table is fixed and cannot be edited.What is deliberately not a signal, each of which wins over the text and over the 421 rule:
- any 5xx: a refusal is not a request to wait;
4.7.1, which greylisting and policy refusals answer, and any reply whose text says greylist or graylist;4.2.x: one mailbox, never the destination;4.5.3: too many recipients for one message;4.4.x:421 4.4.2 timeout exceededis the remote's idle timeout on a slow DATA;- a connection that was refused or timed out, a DNS failure, and this server's own deferrals.
The window and the probe. The first slow-down reply opens a window of
OutboundBackoffMinutes. While it is open a batch for that destination is held with4.4.5, and the application log says:Delivery to <target> deferred: the destination asked this server to slow down at <time> (<reply>); next attempt after <time>.When the window has passed, the first delivery goes through as the probe. If it is delivered, or fails with anything that is not a slow-down reply, the window is cleared. If it is answered with a slow-down reply, the window doubles, up to
OutboundBackoffMaxMinutes. A probe nobody ended - a delivery thread that died with it - goes stale after fifteen minutes and the next delivery takes it over.Setting Default Range Meaning OutboundBackoffMinutes20 to 10080 The first window. 0turns the back-off offOutboundBackoffMaxMinutes601 to 10080 The ceiling a doubling window stops at The two refuse to cross each other on every surface: to move both, move the ceiling first when raising and the base first when lowering.
A relay is one destination. With
SMTPRelayerset, or a route through a smart host, one slow-down reply from it holds every message that goes through it for the window.One miss, stated in the document. The delivery client keeps a recipient's first non-fatal reply, so a genuine 421 from a second MX host after a plain 4xx from the first is not seen for that recipient.
Connection ceilings and the tenant share
Both use the same counting semaphore, and it never blocks: a delivery thread that waited for a slot would be a thread not delivering anybody else's mail. Over the ceiling the batch is deferred one to five minutes.
Setting Default Range What it bounds MaxOutboundConnectionsPerDestination0, off0 to 1000 Sessions open at once to one target that no remote domain policy sets max_connectionsforMaxOutboundConnectionsPerTenant0, off0 to 1000 Deliveries one tenant may have in flight at once The per-domain ceilings are on the remote domain policy, whose TLS half is in Requiring TLS:
Policy field 0 means Beyond it Largest message (KB) no limit of ours Not attempted: no connection is opened, and the message is bounced at once with 5.3.4, naming the policy and both sizesSimultaneous connections unlimited Deferred with 4.4.5for one to five minutes. The count covers every domain the policy's pattern matchesMessages a minute unlimited Deferred with 4.4.5for one to five minutes. Independent ofMaxOutboundPerDestinationPerMinute; both applyA policy's connection ceiling wins over the server-wide one wherever the policy sets it.
Who a tenant is. The hosted domain of the envelope sender, with an alias domain resolved to its domain. A sender this server does not host - mail relayed for an IP range or a route, forwarded external mail, the null sender - is the one tenant
(external), so relayed mail cannot starve the hosted domains either.The tenant share is not a volume control. It is a share of connections, bounded by
MaxDeliveryThreads: a share larger than the thread count is no share at all. The per-account sending limits are the volume control. This server's own delivery reports have the null sender, so they share(external)with relayed mail.Where the settings are. The four new settings are on the Control Panel's Delivery of e-mail page beside
MaxOutboundPerDestinationPerMinute(Windows), on the Deck's Settings view, over COM asSettings.OutboundBackoffMinutes,OutboundBackoffMaxMinutes,MaxOutboundConnectionsPerDestinationandMaxOutboundConnectionsPerTenant(Windows), and over REST on/api/v1/settingsasoutbound_backoff_minutes,outbound_backoff_max_minutes,max_outbound_connections_per_destinationandmax_outbound_connections_per_tenant. Inhm_settingsthe two connection rows are namedMaxOutboundConnsPerDestinationandMaxOutboundConnsPerTenant, because the column holds thirty characters.A deferral this server chose is not counted
When every non-fatal failure of a delivery pass was one of this server's own deferrals, the message's next try is set to the longest deferral of the pass with the tries counter untouched, and the log says so:
Delivery deferred by this server's own shaping, not by a remote server. Scheduling it for later delivery in N minute(s); the attempt is not counted against the retry limit.When any recipient of the pass failed at a remote, the counted path runs as it always has. Before this, with a small retry count, a message could be bounced having never been offered to anybody.
The hold is bounded. A message queued for longer than the span its counted schedule would have allowed - (retries + 1) times the minutes between tries, from the time it was accepted - takes the counted path from then on, so that a destination that says "slow down" for ever still ends in a bounce:
Deferred by this server's own shaping, but it has been queued for N minute(s), longer than the M minute(s) its retry schedule spans. This deferral is counted, so that the message is not held for ever.A held pass is a first pass only once. DKIM and ARC signing, the mirror copy and a rule's policy notice happen once, however many passes a message is held through. Nor does a held pass forward, copy, reply or call a rule's script function again: those four actions run once per counted attempt.
IP warm-up
Schema 6093. A schedule holds one outbound address to a daily ceiling of recipients per provider, rising day by day along a fixed plan; the queue sends up to the ceiling and holds the rest, usually until the next UTC day. Nothing is held until an administrator makes a schedule.
A schedule is about one address, and the address has to be bound. It is the local address a delivery leaves from: a global rule's bind to address action when one applies, otherwise
SMTPDeliveryBindToIP(Bind outbound connections to IP,smtp_delivery_bind_to_ip). A delivery that binds no address leaves from whatever the operating system chooses, and no schedule is consulted for it. So a schedule does nothing, even enabled, until the address is configured on the host and one of those binds it. The server can check the setting and nothing else: the listing saysin_bind_setting, and the Deck warns when it is false.The provider is decided from the mail host, never from the recipient's domain. A company on Google Workspace is reached through Google's MX hosts, and it is Google that judges the address. The first host the batch will try decides.
Provider Key Mail host names Google googlegoogle.com,googlemail.com,aspmx.l.google.comMicrosoft microsoftoutlook.com,hotmail.com,protection.outlook.com,olc.protection.outlook.comYahoo yahooyahoodns.net,yahoo.com,aol.comApple appleicloud.com,apple.comProton protonprotonmail.ch,proton.meEvery other provider othereverything else, counted together at five times the named providers' ceiling Mail relayed through a smart host that is not one of those names is
other, whoever the smart host delivers it to.The plans, in recipients per named provider per day. After a plan's last day there is no ceiling, and the schedule holds and counts nothing.
Day of standardCeiling other1 50 250 2 100 500 3 200 1,000 4 500 2,500 5 1,000 5,000 6 2,000 10,000 7 5,000 25,000 8 to 10 10,000 50,000 11 to 14 20,000 100,000 15 to 21 50,000 250,000 22 to 30 100,000 500,000 The plans side by side:
Plan Length Day 1 Last day Rule standard30 days 50 100,000 the table above gentle60 days 25 50,000 half of each ceiling, twice the days fast15 days 100 200,000 twice each ceiling, half the days Days are UTC days, whatever the server's time zone, and a start later than today is refused.
Recipients, not messages. A message to forty people at Gmail is forty to Google. Before a delivery connects, its recipients reserve room out of the day's allowance; when the attempts are over, every recipient that was delivered is added to
sentand the reservation is let go. A recipient that was not delivered is not counted. So a provider that refuses the warming address is offered as many recipients a day as the queue has; its slow-down replies are answered by the back-off. The counts are written tohm_warmupcountsat every change, so a restart resumes the day. Local delivery is not counted.The hold. Recipients that do not fit - the last of the batch, not chosen by any priority - are marked
4.4.5. When the day's ceiling was reached, the next try is one to five minutes after the next midnight UTC:SMTPDeliverer - Message 1234: 1 recipient(s) held by the IP warm-up of 192.0.2.10: day 1 of its standard schedule allows 50 recipient(s) a day at Google, and today's have been sent. Next attempt after midnight UTC, in 723 minute(s); the attempt is not counted against the retry limit.How long a backlog is carried. With the default schedule - four retries, sixty minutes apart, a five-hour span - a recipient queued in the morning comes back on each of the next five UTC days and is returned if the fifth still has no room. Send no more from a warming address than the sum of those days' ceilings, or raise the retry count for the duration of the warm-up.
Managing schedules. On the Control Deck's Delivery queue page, in the Warm-up box, or over REST. The desktop Control Panel has nothing for it, and there is no COM interface.
Route What it does GET /api/v1/warmupToday's UTC date and the schedules, each with its day, in_bind_setting, and per providersent,deferredandceilingPOST /api/v1/warmupAdds one: address(required),plan(standardby default),started(YYYY-MM-DD, today by default),enabledPUT /api/v1/warmup/{id}Changes plan,startedorenabled. The address cannot be changedDELETE /api/v1/warmup/{id}Removes a schedule and its counts GET /api/v1/warmup/{id}/historyThe counts, newest first, with the day and ceiling in force then Reading takes
view_queueand changing takesmanage_queue; a credential confined to named domains is refused. A second schedule for one address is409. Back up settings carries the schedules but not the counts.When the database does not answer, the queue does not stop: schedules that cannot be read (
7210) and a day's count that cannot be read or started (7211,7212) leave the delivery going as though the address had no schedule. A count that cannot be saved (7213) is kept in memory; if the service restarts first, the day can go past its ceiling by that much.Where to see it
GET /api/v1/queue/shaping(view_queue, server-wide): the open back-off windows - destination, since, until, the window's minutes, the reply and the host that gave it, how many deliveries it has held, whether a probe is out - and the connections open under each ceiling and each tenant.DELETE /api/v1/queue/shaping/{destination}(manage_queue) clears one window once you have fixed the cause:200with{"cleared":true},404when no window is open for that name, and an event on the audit trail.- The Deck's Delivery queue shows the same as Slowed destinations, with a Resume button, and the connection counts as they were when the page was drawn.
- The application log says, for each batch held, which rule held it and until when.
What is per process. The windows and the connection counts live in memory. A restart forgets every window, which costs at most one more slow-down reply per destination. Nothing is shared between the nodes of a warm-standby pair.
What it does not do
- The signal phrases, the providers and the plans are fixed. A sixth provider, or a plan of your own, is not possible.
- No per-tenant volume quota beyond the per-account one: this shares connections, not messages a day.
- No pool of addresses. A schedule is about one address, and mail that did not fit waits.
- No reputation feedback. The ceilings rise by the calendar, not by how the providers are answering.
- No delayed delivery report. A sender learns of a long hold only when it ends in a bounce.
- A deferral by a rate or a ceiling costs one delivery pass per message every one to five minutes for as long as the backlog lasts.
- The warm-up counts are not exported as metrics.
Not proved by any test, according to IpWarmup.md: room held by deliveries in progress; a later host of the other address family skipped for being reached from another warming address; what a database failure does (
7210to7213); the audit trail's entry; the configuration backup's round trip of the schedules; and an MX lookup's own host names reaching the classification, since every delivery in the fixture goes through a route.Where to read more
- Operator documents at the v6.3.4 tag: OutboundShaping.md, IpWarmup.md and RemoteDomains.md.
- Wiki: Outbound Shaping and Routes and Relays.
- Public documentation: 26.7 The delivery queue and the retry schedule and 14.1 Sending through a smart host.
- On this forum: The Control Deck, Upgrading to 6.3.4, Requiring TLS for the remote domain policy's TLS half, and DKIM rotation, DMARC and TLS reports, complaints, bounces and the reputation watch for bounces, complaints and block lists.
- The remote domain policy's largest message:
Hello! It looks like you're interested in this conversation, but you don't have an account yet.
Getting fed up of having to scroll through the same posts each visit? When you register for an account, you'll always come back to exactly where you were before, and choose to be notified of new replies (either via email, or push notification). You'll also be able to save bookmarks and upvote posts to show your appreciation to other community members.
With your input, this post could be even better 💗
Register Login