critical
Each of the six methods appears in the manual separately and looks like an equal alternative. Chapter 9 gathers them in one place and says the key thing: they are ordered. The list is not a set of options but a resolution rule.
The ladder from top to bottom. The *vmta merge variable in the message itself; a choice at submission — an API call or the X-Virtual-MTA header; a pattern rule from <pattern-list>; a stream bound to the listener; the source default default-virtual-mta. What is higher wins; what is lower applies only when nobody above has spoken.
Something non-obvious follows from this. A platform that sets the header cannot be overridden by a node setting: the configuration stands below the header. And vice versa — a default written in a source looks like an assignment, but in fact it is a fallback that stops working the moment the sender starts sending the header.
A pool is selected in exactly the same ways. The only difference is the name: the pool name is written instead of the stream name. That is why stream and pool names live in one namespace, and the engine rejects a collision rather than resolving it.
One thing about this setup is important to remember: the header stands above both the selection list and the default. A platform that sends X-Virtual-MTA bypasses all our routing rules — and this is allowed deliberately, so that warm-up can name the address in the message.
The ladder has no values of its own — the directives at each step set them. What to choose depends on what the sender controls:
| Sender | How to select | Why |
|---|---|---|
| ours, sends headers | X-Virtual-MTA | the most direct and precise: a stream per message |
| someone else’s, cannot do headers | mail-from by return address | requires nothing from the sender |
| someone else’s, has its own submission address | by-smtp-source-ip | does not grow with the number of addresses |
| everything else | default-virtual-mta | a fallback, not an assignment |
Methods can and often should be mixed; you just need to understand that on a match the higher one wins.
# four steps in one configuration — top to bottom by precedence <pattern-list mta-hub-senders> mail-from /@shop\.example\.com$/ virtual-mta=shop-example-com mail-from /@news\.example\.com$/ virtual-mta=news-example-com </pattern-list> <source {pmta-auth}> process-x-virtual-mta yes # step 2: the header is allowed pattern-list mta-hub-senders # step 3: pattern rule default-virtual-mta mta-hub-pool # step 5: fallback</source>
what people get wrong and how it ends3 mistakes▶
§ 9.4
§ 9.4
§ 9.3
3 relations
Three pool rules the manual states directlyA pool is selected in the same six ways as a sending address — which is why they share names. § 9.3How to enable DKIM signing from scratchA key inside a stream signs only the mail that actually got into that stream. § 10.3.2process-x-sp-subaccount-id<signals>The same order of precedence as for stream selection: the message header is stronger than the configuration. inferenceprocess-x-sp-subaccount-id<inline-seeding>The same technique: the message header overrides what the configuration sets. inferenceReserved merge variable namesA related mechanism: they work together. § 11.19.4.2Replacing a recipient with one or several othersExpansion happens after the stream is chosen: the stream is chosen by the original recipient, while the message leaves addressed to others. § 13.9
pmtaacctfind --output csv[vmta,orig,rcpt] /var/log/pmta/acct-*.csv | head
3 articles
◇Reserved merge variable namesSome merge variable names are taken by the engine: you cannot give your own variables these names.◇Warm-up built into the engineA warmed-up address hands the first N messages of the day for each receiver to its cold partner; the partner sends them itself and builds history.◇The sending address — what it is made ofA named set of everything that is a property of the address: which IP the message leaves from, how the node introduces itself, what it signs with and how fast it sends.