Moving from PowerMTA

  • January 8, 2024

A while ago we published a blog on the easy conversion process to move from other MTA configs to KumoMTA. In this post, I want to touch on something we are asked about often - how to move your PowerMTA configuration to KumoMTA.

As mentioned in that first highlighted post, we used common terminology and functionality when designing KumoMTA, but also made modernizations where they were needed.  Moving your platform from PowerMTA (PMTA) to KumoMTA should be a breeze if you remember a few essential concepts.

1) "VMTA" is roughly equivalent to "Egress Source": If you want to control delivery throttling and other controls based on IP address like you would with a VMTA, then you should manage those controls at the egress source level. 

2) Many PMTA simple "one-liners" are easy to add to a config, but lack flexibility.  Often you will find that KumoMTA handles these in different files for greater flexibility.

3) The flat-file PMTA configuration does not allow for conditionals or iteration. KumoMTA can often condense your config with Lua logic.

4) Many settings in PMTA were added as bug-fixes or work-arounds to common situations like "Run-as-root" and "delete-file-holders".  These are not needed in KumoMTA because we took care of all of those issues in the underlying code.

5) Just because it made sense to do it that way in PMTA, does not mean it is the most efficient way. Be prepared to ask yourself, "Why did we do it this way?".  KumoMTA may offer a better, more effective solution.

Let's take that last point first.  Something that is very common in PowerMTA configurations is the use of macros and MX Rollups.  While macros can be used for a number of things, MX Rollup is the most common. It is so common in fact, that we just built it in as a native function of KumoMTA.  

KumoMTA automatically performs MX rollup for all destination domains and queues messages based on the rolled-up MX fingerprint, meaning that without manual configuration all messages destined for the same MX group will be queued and throttled together, regardless of destination domain. This makes it much easier to manage traffic to large white-label mailbox providers without any manual configuration that can be incomplete or outdated. Of course, you can override these if you want to make exceptions to one particular domain, but that is much less complicated than including hundreds of rollup domains.

Now that we have thrown out the bulk of most PMTA configs with embedded automation, let's talk about the rest.

Listener Configuration:

When multiple listeners are in play, PMTA requires them to be defined repeatedly, even if they are almost identical. To explore the power of iteration, consider this common listener configuration below where there are five nearly identical listener sources with different ports that then reference these sources in listener statements.  In PMTA it might look like this:

<source myvmta1>
  smtp-service yes
  process-x-job yes
  allow-starttls true
</source>
<source myvmta2>
  smtp-service yes
  process-x-job yes
  allow-starttls true
</source>
<source myvmta3>
  smtp-service yes
  process-x-job yes
  allow-starttls true
</source>
<source myvmta4>
  smtp-service yes
  process-x-job yes
  allow-starttls true
</source>
<source myvmta5>
  smtp-service yes
  process-x-job yes
  allow-starttls true
</source>

smtp-server-tls-certificate /etc/pmta/ssl/mydomain.com.cert
smtp-server-tls-ca-file /etc/pmta/ssl/mydomain.com.chain

smtp-listener 10.1.1.5:2525 source=myvmta1

smtp-listener 10.1.1.5:2530 source=myvmta2
smtp-listener 10.1.1.5:2535 source=myvmta3
smtp-listener 10.1.1.5:2540 source=myvmta4
smtp-listener 10.1.1.5:2545 source=myvmta5

In KumoMTA, this can be written with iteration:

for _, ip_and_port in ipairs{'0:2525', '0:2530', '0:2535', '0:2540', '0:2545'} do
  kumo.start_esmtp_listener{
      listen = ip_and_port,
      tls_private_key = "/opt/kumomta/etc/tls/mydomain.com/privkey.pem",
      tls_certificate = "/opt/kumomta/etc/tls/mydomain.com/fullchain.pem",
  }
end

Using iteration, this produces a much smaller, easier to use config.

Throttling Automation:

For users who have made use of throttling backoffs, we have a better solution with similar configuration. KumoMTA features native cluster-aware traffic shaping with automation, meaning that in proxied environments the nodes can share throttles and respect limits imposed by mailbox providers without having to manually divide throttles by node count.

In PMTA a typical backoff looks something like this, and is locally static on each node. 

<smtp-pattern-list backoff>
  reply /try again later/ mode=backoff
<smtp-pattern-list>
<domain hotmail.com>
  smtp-pattern-list backoff. 
  backoff-retry-after 1h
  backoff-max-msg-rate 300/h
<domain>

The KumoMTA equivalent is more compact and is shared cluster-wide.

[["hotmail.com".automation]]
  regex = "/try again later/"
  action = {SetConfig={name="max_message_rate", value="300/h"}}
  duration = "1 hour"

DKIM Signing:

Another other key item in any configuration is DKIM signing.  A typical PMTA config may look something like this:

<source 0/0>
process-x-dkim-options  yes
<source>
<domain *>
  second-dkim-sign yes
  second-dkim-identity @myesp.com
</domain>
domain-key dk1024, example.com, /etc/pmta/dkim/dk1024.example.com.pem

In KumoMTA, DKIM keys and signing policy, including secondary signing, is most commonly managed with the DKIM Helper config.

[domain.'example.com']
  selector = 'dk1024’
  filename = "/opt/kumomta/dkim/example.com.pem”
  policy = "SignAlways"
  additional_signatures = ["MyESP"]

[signature."MyESP"]
  policy = "Always" # Always add this signature
  domain = "myesp.com"

HTTP API:

Other settings are functionally similar but implemented in a more efficient way. Take HTTP API for instance. In PMTA a typical configuration might looks like this:

http-mgmt-port 8080
http-access 0/0 monitor
http-access 10.1.1.100 admin
http-tls-ca-file /etc/ssl/certs/host-ca_chain.cert

In KumoMTA, it is enabled as a listener.

kumo.start_http_listener {
  trusted_hosts = { '10.1.1.100', '127.0.0.1'},
  listen = '0.0.0.0:8080',
  hostname = 'mail.example.com',
  use_tls = true,
  tls_certificate = '/path/to/cert.pem',
  tls_private_key = '/path/to/key.pem',
}

Get a second opinion

Finally, since you are moving from one MTA to another this might be a great time to review all the functions and the reasons they are there.  As mentioned in bullet 5 above, just because you did it that way before, does not mean it is the best way now.

The team at KumoMTA can provide paid consulting services to help you get to an effective and efficient deployment quickly and we can help you cut through all the investigation and testing.  If you have the time to spend on doing the conversion yourself but want to get community feedback on your efforts, feel free to join the Discord here.