Skip to content

Docs / Config

The PowerMTA config file (/etc/pmta/config) explained

Updated 2026-06-05· 8 min read· bring-your-own-license

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:

BlockPurpose
<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:

DirectiveControls
max-smtp-outSimultaneous connections to that domain
max-msg-rateMessages per unit time (e.g. 100/min)
max-msg-per-connectionMessages sent down one connection before reconnecting
retry-afterWait before retrying a temporarily-deferred message
bounce-afterHow long to keep retrying before hard-bouncing
dkim-signWhether 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:

  1. Global directives first — postmaster, logging, accounting, the monitoring port and its access list.
  2. Source and smtp-user blocks next — the security boundary, with the deny-by-default rule before the trusted exceptions.
  3. domain-key lines for DKIM — grouped together so signing keys are easy to audit.
  4. Virtual MTAs, then pools — define the IPs, then the groupings that reference them.
  5. Domain policy — the broad default first, then per-provider overrides.
  6. 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 that denies relaying, one trusted source subnet, a couple of virtual MTAs, and a default policy. Tune from your accounting logs rather than pasting a large config you don't fully understand.

Related