Knowledge base

The stream is chosen by matching the envelope or a header

pattern-based selection
Rules on the node instead of an agreement with the sender

The most expressive of the six methods and the only one that does not need the sender’s consent. The rule list is declared once with a <pattern-list> block and attached to a source with the pattern-list directive. Inside the list, rules match the envelope return address (mail-from), the recipient address (rcpt-to) or an arbitrary header (header) — and each names a stream or pool.

Order matters and is stated: the first match wins. That means the list is not a set of independent rules but a priority queue. The most likely patterns are put first, and a rule added at the end may never fire.

Exactly one list is attached to a source. Everything that source needs has to be expressed inside it; for several platforms on one source — in one shared ordered list.

Conditional rules are allowed. A rule can open a nested list — “the return address is this AND the recipient is that” — with an asterisk meaning “everything else.” A separate technique is call-to:, a jump to another named list.

The dialect is Perl regular expressions (PCRE), and by default they are case-insensitive. Case sensitivity can be restored with the (?-i) modifier inside a pattern.

The main trap is a recipient-based rule in a message with several addressees. The manual says it directly: if several recipients match under rcpt-to, the stream of the first match applies to the whole message. So recipient matching works honestly only on messages with one addressee — and the vendor warns that the behavior may change.
What the Hub does. It uses it, and this is one of the two rungs it writes itself. The working configuration gets a selection list: for each “sending domain + address” pair a return address rule is added that routes the message to the right stream. The list is attached to the submission source, which also gets a default pointing to the pool — in case the return address matches none of the rules.

The Hub’s rule order is flat: one per domain, no overlaps, so “first match” creates no surprises here. The Hub does not write recipient rules — precisely because of the behavior with several addressees.

The Hub rewrites the return address to bounce@bounce.<domain> for bounce reception, but the rules match the original one: verified on a node on Sep 15, 2026 — messages submitted with the shared login and no X-Virtual-MTA went through the streams of their own domains, while accounting already showed the rewritten address for them.

Recommended values

The mechanism has no values of its own: the values are the rules themselves. What is worth remembering when putting a list together:

What to match When to use it What you risk
return address almost always: it is stable and belongs to you nothing
header when only the header distinguishes the sender a header is easy to change on the sender’s side
recipient only for messages with one addressee with several recipients the stream is taken from the first match

A rule can set not only a stream but also the recipient priority (recipient-priority=N, scale 0–100, default 50) — separately or together with the stream.

From the manual here come the three rule types, “first match,” the PCRE dialect and the caveat about several recipients. The “when to use what” table is ours.
Example configuration
# the manual’s order: the list is declared above the source that refers to it
<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}>
    pattern-list mta-hub-senders
    default-virtual-mta mta-hub-pool
</source>
This is how the Hub assembles the working configuration. A message with a return address in the shop’s domain will go through its own stream; everything that did not match goes to the pool from the last line. The manual requires the list to appear above the source; engine 6.0 also resolves a forward reference, but the manual’s order is more reliable on any version.
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▶

Matching the recipient in a mailing

§ 3.3.14.3

What it costsWith several recipients in a message, the stream of the first match applies to all of them — including those that did not match the rule. Part of the mailing goes out from someone else’s address, and in accounting it looks like a routing error with no cause.
How to spot itRecipients of one domain have a stream in accounting that was assigned to a completely different domain.
How to do it rightMatch the return address, not the recipient. If separation really must be by recipient, submit one addressee per message.

Adding a rule to the end of the list

§ 9.4.2

What it costsThe first match wins, so a broad rule higher up permanently shadows a narrower one added below. The new rule is in the configuration and never fires.
How to spot itThe rule was added, the apply succeeded, and the behavior did not change.
How to do it rightTreat the list as a priority queue: narrow patterns higher, broad ones lower. The generator should assemble the list in this order, not in the order rules were added.

Putting the list below the source in the file

§ 3.3.7.42

What it costsThe manual requires the list to be declared before the source that refers to it. Engine 6.0 resolves a forward reference (verified on a node on Sep 15, 2026), but this is not promised: on another version the configuration may fail to apply.
How to spot itOn 6.0 — none. After a version change the reload fails, and the error line points to the source, not to the list.
How to do it rightKeep the order “lists first, then sources,” as the manual requires.
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 list itself has no inheritance: the rules act inside their block. What is inherited is the attachment — pattern-list is written in <source> and merges by the source rule, where a narrower block overrides a wider one. From “one list per source” follows something important: a narrow block does not add rules to the wide block’s list but replaces the whole list.
How to check on the node
The check is the same for all rules — by the result in accounting; the engine does not comment on the rules separately:
pmtaacctfind --output csv[vmta,orig,rcpt] /var/log/pmta/acct-*.csv | head -50
The orig field is the envelope return address. If the source rewrites it with rewrite-list, accounting holds the already rewritten address, while the rules matched the original one: judge the stream in vmta by the domain before rewriting. If the stream came from the default, no rule matched: most often an escaped . or the $ anchor was forgotten in the pattern, not the logic itself.
In the Hub
Cost of a mistake
reversible: harms only while it is set
Scenarios
Sending address rotation · Sender isolation