Knowledge base

Six ways to choose a stream and the order among them

critical

VirtualMTA selection precedence
Who decides which address a message leaves from

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.

The main trap. No report says which of the six methods chose the stream for a particular message. Accounting has the vmta field — that is, the result — but the cause is nowhere. When a message left from the wrong address, the question has six possible answers and not a single diagnostic one. You have to check by going through them from top to bottom.
What the Hub does. It uses four of the six steps and knows it. In the main file of the working configuration the general 0/0 source gets default-virtual-mta with the value by-smtp-source-ip — the stream is determined by the address the platform connected to — and a selection list by sender domain (mail-from in <pattern-list>). In the addresses file the general login’s source gets the same list and a default to the submission pool. The header is allowed: process-x-virtual-mta is set to yes in every source of the working configuration. The base configuration, installed before any pairs exist, has neither the list nor the default.

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.

Recommended values

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.

The table is ours, from the Hub’s practice. From the manual here are the order of the steps itself and the “higher wins” rule.
Example configuration
# 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>
A message with the header X-Virtual-MTA: news-example-com leaves through that stream even if the return address belongs to the shop: the header stands above the list. A message without the header and with someone else’s return address goes to the pool — that is the last step.
yellow — — directive names, blue — blocks. They are highlighted the same way in the text of the card.
▲

Common mistakes and problems

what people get wrong and how it ends3 mistakes▶

Treating the source default as an assignment

§ 9.4

What it costsThe platform was given “its own” stream with the default-virtual-mta line, and this works exactly until the day the platform starts setting X-Virtual-MTA — for example, after an update. Messages leave from another address, and not a single line of the configuration has changed.
How to spot itIn accounting, one sender suddenly shows someone else’s stream name; the configuration was not touched, and the investigation hits “everything is correct.”
How to do it rightEither do not allow the header for this source (process-x-virtual-mta no), or stop treating the default as an assignment and choose the stream at the same step where the sender does.

Expecting a node setting to override the header

§ 9.4

What it costsThe configuration stands below the header on the ladder. A pattern rule added “to be sure the sender is moved to the right address” does not fire at all if the sender sends the header.
How to spot itThe new rule in <pattern-list> is visible in the configuration, the stream is the same, and it seems the list is not being read.
How to do it rightCheck whether X-Virtual-MTA arrives. If it does, remove the header permission for this source or come to an agreement with the sender, rather than complicating the list.

Naming a pool after an existing stream

§ 9.3

What it costsThe engine rejects the pool definition as a whole: selection works through the same headers and directives, and a name collision cannot be resolved. On apply this is not a warning but a failed reload.
How to spot itThe configuration does not apply, and the cause points to the pool rather than to the line that was edited last.
How to do it rightKeep stream and pool names in one list and check uniqueness before writing the file — it costs the generator one check.
Compatibility
PowerMTA 6.0
described in the manual of this version · platform: any
The vendor does not state the first version for every directive — there is no note here. The name also appears in the 5.0 edition of the manual.
Inheritance
The ladder itself has no inheritance: it is resolved anew for each message. The directives at the steps inherit — default-virtual-mta, process-x-virtual-mta and pattern-list are written in <source> and merge under the usual source rule: a narrower block overrides a wider one.
How to check on the node
There is no ready answer to “which method chose the stream” — there is only the result. Look in accounting, at the stream name field:
pmtaacctfind --output csv[vmta,orig,rcpt] /var/log/pmta/acct-*.csv | head
If the stream is not the one you expected, check from top to bottom: does the message carry X-Virtual-MTA; does the return address fall under a list rule; which node address did the connection come to; what is set as the source default. The very first “yes” is the cause — nobody looks further down.
In the Hub
Cost of a mistake
reversible: harms only while it is set
Scenarios
Sending address rotation · Address warm-up · Sender isolation
Priority
critical