Docs / Config
The PowerMTA config file (/etc/pmta/config) explained
The PowerMTA configuration file lives at /etc/pmta/config. It is organized into global directives plus blocks: <source> defines which IPs may relay, <virtual-mta> binds sending IPs, <virtual-mta-pool> groups them, and <domain> sets per-provider policy. Edit the file, run pmta reload to apply changes without dropping the queue, and verify with pmta show status.
Almost everything PowerMTA does is driven by a single file: /etc/pmta/config. It is powerful and unforgiving — a too-open source block or a missing domain policy quietly hurts deliverability. This page maps the file's structure so you can read and edit it with confidence, the right way for legitimate, opt-in sending. The single idea that makes sense of the whole file is its scope model, so that’s where the real understanding starts.
Anatomy of the file
The config is a mix of global directives (one per line, at the top) and blocks (delimited by angle-bracket tags). The main block types:
| Block | Purpose |
|---|---|
<source IP> | Who may relay through PowerMTA, and how (auth, relaying, vMTA selection) |
<virtual-mta NAME> | Binds an outbound IP + HELO hostname to a named sender |
<virtual-mta-pool NAME> | Groups virtual MTAs so traffic can be spread across IPs |
<domain NAME> / <domain *> | Per-recipient-domain delivery policy (rates, retries, back-off) |
The scope cascade
PowerMTA’s configuration is declarative and scoped: a directive set at a broad level applies
everywhere beneath it unless a narrower block overrides it. The hierarchy runs from the widest to the most
specific — global, then <virtual-mta-pool>, then <virtual-mta>, then
<domain> — and the most specific match wins. Set max-smtp-out 20 globally and it
governs everything; set it again inside a <domain gmail.com> block and that value applies only to
Gmail, leaving the global default in place for everyone else.
Internalising this saves enormous confusion, because it means you rarely repeat yourself: you state a sane default
once at the top, then write small, specific blocks only for the cases that differ. A strict provider that needs
slower sending gets its own <domain> block; everything else inherits the default. When a setting
seems not to take effect, the cause is almost always a more-specific block overriding it somewhere lower down —
so you read the file from the most specific match outward, not top to bottom.
Global directives
Set before any block — these govern the whole instance:
# /etc/pmta/config — global directives
postmaster [email protected]
http-mgmt-port 8080 # monitoring UI / API
log-file /var/log/pmta/pmta.log
<acct-file /var/log/pmta/acct.csv>
records d, b, rb # delivery, bounce, rebound
</acct-file> Source blocks
The first line of defense. Deny relaying by default, then allow only your trusted submission subnet:
<source 0/0> # matches everything
always-allow-relaying no
allow-unencrypted-plain-auth no
process-x-virtual-mta yes
smtp-service yes
</source>
<source 10.0.0.0/24> # your app / MailWizz subnet
always-allow-relaying yes
default-virtual-mta pool-main
</source> What source blocks control
The <source> block is the security boundary of the whole server, so it earns a closer look. It
decides, per submitting IP range, three things: who may relay (always-allow-relaying),
whether authentication is required (require-auth, paired with
<smtp-user> blocks), and what the submitter may do — select a virtual MTA
(process-x-virtual-mta), set message size limits (max-message-size), or hide the
originating host (hide-message-source, remove-header Received).
The cardinal rule is to deny by default. A <source 0/0> that allows relaying is
an open relay — the fastest way to get your IPs blocklisted as spammers feed mail through you. The correct pattern
is the one shown above: a restrictive 0/0 that denies relaying and unauthenticated plain auth, plus a
narrow trusted subnet (your application or MailWizz host) that may relay. If you accept authenticated submission
from the wider internet, require auth and define <smtp-user> credentials rather than trusting an
IP range you don’t fully control.
DKIM signing in the config
DKIM signing is wired in with the domain-key directive, which maps a selector and domain to a private
key file:
# domain-key SELECTOR,DOMAIN,/path/to/private-key.pem
domain-key s2026,example.com,/etc/pmta/dkim/example.com.pem
# then enable signing in the relevant domain scope
<domain *>
dkim-sign yes
</domain>
You publish the matching public key in DNS at s2026._domainkey.example.com, point
domain-key at the private half, and turn on dkim-sign in the domain scope. Multiple
domain-key lines let you sign different domains with their own keys. The full authentication picture —
SPF and DMARC alongside DKIM — is covered in SPF, DKIM & DMARC.
Virtual MTAs and domain policy
Bind your IPs, then group and govern them. Per-domain blocks let you slow down for a strict provider without throttling everyone:
<virtual-mta vmta-1>
smtp-source-host 203.0.113.10 mail.example.com
</virtual-mta>
<virtual-mta-pool pool-main>
virtual-mta vmta-1
virtual-mta vmta-2
</virtual-mta-pool>
<domain *> # default policy
max-smtp-out 20
max-msg-rate 100/min
retry-after 10m
</domain> See virtual MTAs and IP pools for the full treatment of each.
Domain policy directives
The <domain> block is where deliverability tuning lives, because it controls how hard you push
each receiver. The directives that matter most:
| Directive | Controls |
|---|---|
max-smtp-out | Simultaneous connections to that domain |
max-msg-rate | Messages per unit time (e.g. 100/min) |
max-msg-per-connection | Messages sent down one connection before reconnecting |
retry-after | Wait before retrying a temporarily-deferred message |
bounce-after | How long to keep retrying before hard-bouncing |
dkim-sign | Whether to DKIM-sign mail in this scope |
The pattern is a conservative <domain *> default plus a handful of per-provider overrides for
the mailbox providers that need gentler handling. A strict receiver might get a lower max-smtp-out
and a longer retry-after; a tolerant one can run faster. This per-domain control is exactly what lets
you slow down for one provider without throttling the entire server — and it pairs with back-off rules, covered in
throttling and back-off, which automatically ease a queue that
starts seeing deferrals.
Accounting and monitoring
PowerMTA records every delivery event to CSV accounting files, and these are your primary source of truth for
tuning. The <acct-file> block chooses which record types to log and how to rotate them:
<acct-file /var/log/pmta/acct.csv>
records d, b # d=delivery, b=bounce
move-interval 1d
delete-after 7d
max-size 50M
</acct-file>
A second accounting file for transient errors (a diag.csv with records t) is invaluable
for diagnosing soft bounces and deferrals. Alongside the logs, http-mgmt-port exposes the monitoring
UI and API — lock it down with http-access so only trusted IPs can reach it. The discipline that
separates good operators from frustrated ones is simple: tune from the accounting data, not from
guesswork, adjusting domain policy in response to what the logs actually show.
Splitting the config with include
A production configuration gets long, and the include directive keeps it manageable by pulling in
separate files that are all parsed into one runtime config. A common split is a common base file plus per-host or
per-purpose fragments — one for IP pools, one for domain groups, one for pattern lists — so each piece stays
readable and a change touches only the relevant fragment.
This pays off most across multiple servers: keep the settings that are identical everywhere in a shared file, and isolate the per-host differences (the IPs, the hostnames) in a small included file. Maintenance then means editing one common file and a tiny host-specific one, rather than hand-reconciling a giant monolithic config on every box.
Validate and reload
Never restart blindly. Apply changes gracefully and confirm health:
pmta reload # apply without dropping the queue
pmta show status
pmta show queues Reload vs restart, safely
There’s an important difference between reloading and restarting. pmta reload re-reads the
configuration and applies changes gracefully, without dropping the queue or interrupting active
deliveries — it’s what you use for routine edits. A full restart stops and starts the daemon, which is heavier and
rarely necessary for a config change. Before either, validate: a syntax error in the config can prevent the service
coming back, so you check the file is valid first, reload, then confirm with pmta show status and
pmta show queues that the instance is healthy and the queues look right.
The habit to build is never editing a production config without that validate-reload-verify loop. A reload that fails on a typo at 2am, taking your sending offline, is entirely avoidable — and on a busy server the difference between a graceful reload and a blind restart can be thousands of queued messages.
KumoMTA: the same concepts in Lua
If you run KumoMTA instead of PowerMTA, the file looks completely different but the concepts map almost
one-to-one. KumoMTA is configured in Lua — an init.lua script rather than declarative
angle-bracket blocks — so instead of a <source> block you register a listener and its relaying
rules in code, and instead of <virtual-mta> blocks you define egress sources and egress pools that
play the same role: binding sending IPs and grouping them.
Per-domain policy that PowerMTA expresses as <domain> directives becomes, in KumoMTA, throttle and
routing logic applied in Lua — the same levers (connection limits, message rates, retry behaviour) reached through a
scripting model rather than a configuration grammar. The trade-off is familiar: KumoMTA’s Lua is more flexible and
programmable, while PowerMTA’s declarative file is more constrained and arguably easier to read at a glance. The
Auto PMTA Configurator generates working configuration for either engine, so the choice doesn’t change how you get
started.
Common config mistakes
A handful of errors cause most config trouble. The dangerous one is the open relay — a
<source 0/0> with always-allow-relaying yes — which lets anyone send through your
server and gets your IPs blocklisted fast. The silent one is no domain policy: with no
<domain *> defaults, PowerMTA sends as fast as it can and trips provider rate limits. The
self-inflicted one is pasting a large config you don’t understand from a forum, inheriting
settings you can’t explain.
The rest are mechanical: a wrong DKIM key path in domain-key so signing silently
fails, a monitoring port left open to the internet because http-access wasn’t locked
down, and restarting instead of reloading and dropping the queue. Each is easy to avoid once you
know to look for it — which is the whole reason to understand the file rather than copy it.
From baseline to production
PowerMTA installs with a baseline config and the full UsersGuide.pdf, but the baseline is a starting
point, not a production setup. The sane path is to start minimal and hardened — a deny-by-default
<source 0/0>, one trusted submission subnet, a couple of virtual MTAs, a conservative
<domain *> default — then tune outward from your accounting logs as real sending reveals which
providers need gentler handling.
Resist the urge to configure for problems you don’t have yet. A small, well-understood config that you can read top to bottom beats a sprawling one copied from elsewhere, every time — it’s easier to debug, safer to change, and far less likely to hide an open relay or a runaway send rate. Grow the file in response to evidence, and it stays comprehensible as it grows.
How mail gets in: feeding
Before PowerMTA can deliver anything, mail has to reach it, and the config governs that too. The common path is
SMTP submission: your application or mailer (MailWizz, an app server) connects and hands over
messages, and the <source> block for that connecting IP decides whether it’s allowed and what it
can do. This is why the source block is both the security boundary and the entry point — it’s the gate every
message passes through on the way in.
PowerMTA also accepts mail through a pickup (spool) directory and a submission API for applications that prefer to drop files or post over HTTP rather than speak SMTP. Whichever you use, the same downstream config applies once the message is accepted: it’s matched to a virtual MTA, governed by the relevant domain policy, signed if DKIM is on, and queued for delivery. Understanding that the source/feed step is separate from the delivery step is what makes the rest of the file fall into place — sources are about getting mail in, virtual MTAs and domains are about getting it out.
Authenticated submission with smtp-user
When you can’t trust a fixed IP range — submission from the open internet, or from hosts whose addresses change —
you require authentication instead. The <smtp-user> block defines a credential and binds it to a
source profile:
<smtp-user app1>
password a-long-random-secret
source {authenticated}
</smtp-user>
<source {authenticated}>
require-auth true
always-allow-relaying yes
process-x-virtual-mta yes
</source>
Here relaying is granted on the basis of a verified login rather than a trusted IP, which is the safer model for
anything beyond a locked-down private subnet. Pair it with TLS so the credentials aren’t sent in clear, give each
application its own user so you can revoke one without disturbing the others, and keep the passwords as real
secrets — an <smtp-user> credential that leaks is its own kind of open relay.
How to read the file
Put together, the file has a natural reading order that mirrors a message’s journey. Start at the global directives for the instance-wide defaults, then the source and smtp-user blocks to see who may feed mail in and how. From there move to the virtual MTAs and pools to see which IPs send, and finally the domain blocks for how each receiver is treated — remembering the scope cascade means a domain block can override anything broader.
Read in that order, the config tells a story: mail arrives through a source, is assigned a sending identity via a virtual MTA, is shaped by domain policy, signed with DKIM, and logged to the accounting files. Once you see it that way, the angle-bracket blocks stop being an intimidating wall and become a readable description of exactly how your server handles every message.
A recommended file layout
Order isn’t enforced — the parser reads the whole file — but a consistent layout makes a config dramatically easier to maintain, and it mirrors the reading order above. A layout that holds up well:
- Global directives first — postmaster, logging, accounting, the monitoring port and its access list.
- Source and smtp-user blocks next — the security boundary, with the deny-by-default rule before the trusted exceptions.
- domain-key lines for DKIM — grouped together so signing keys are easy to audit.
- Virtual MTAs, then pools — define the IPs, then the groupings that reference them.
- Domain policy — the broad default first, then per-provider overrides.
- Includes — pulled in where they belong, for pattern lists or per-host fragments.
Keeping to a layout like this means anyone who opens the file — including you, months later — finds each kind of setting where they expect it, and the deny-before-allow ordering in the source section makes the security posture obvious at a glance. A tidy config isn’t cosmetic; it’s how you avoid the subtle mistakes that a sprawling, disordered file invites — a stray allow rule lost in the noise, a domain override you forgot was there.
Pair the layout with comments that say why rather than merely what — a note on the reason a particular provider gets a slower rate is worth more in six months than the bare directive, when nobody remembers what prompted it. Future-you, debugging a deferral at speed, will be grateful for the sentence of context that the directive alone can never carry. The config is documentation as much as instruction; treat it that way and it stays an asset rather than a liability as it grows.
The bottom line
The PowerMTA config at /etc/pmta/config is one declarative file built from global directives and
scoped blocks — <source> for who may relay, <virtual-mta> and
<virtual-mta-pool> for sending IPs, <domain> for per-provider policy, plus
domain-key for DKIM and <acct-file> for the logs you tune from. The scope cascade,
where specific blocks override broad defaults, is the concept that makes all of it readable.
Lock sources down to deny-by-default, give every send a sane domain policy, sign with DKIM, validate and
pmta reload rather than restart, and tune from accounting data. KumoMTA expresses the same ideas in
Lua if that’s your engine. If you’d rather not hand-write and harden all of this, the
installer writes a locked-down, minimal config mapped to your IPs and
domains, and the Auto PMTA Configurator maintains it as your setup grows — for legitimate,
opt-in sending.
Frequently asked questions
Where is the PowerMTA config file located? +
The main configuration file is /etc/pmta/config. Supporting files (DKIM keys, include files, pattern lists) usually live under /etc/pmta/ as well, and the full User's Guide is installed at /usr/share/doc/pmta/UsersGuide.pdf.
How do I apply changes to the PowerMTA config? +
Save the file, then run pmta reload to apply changes gracefully without dropping queued mail. Use pmta show status and pmta show queues to confirm the new configuration is live and healthy.
Can I split the PowerMTA config into multiple files? +
Yes. Use the include directive to pull in separate files — for example one per IP pool, per domain group, or for pattern lists. This keeps a large config readable and makes per-environment changes safer.
What's the safest way to start a config? +
Start minimal and hardened: a restrictive
Related