Installation
NitroMTA runs on one Linux host with systemd, on x86_64 or arm64, with glibc 2.34 or later (Debian 12, Ubuntu 22.04, RHEL 9, or newer). It delivers mail directly, so the host needs what any sending host needs:
- outbound TCP to port 25 (many cloud providers block it until asked), and inbound to port 25, where bounces and complaints arrive;
- a public address for each IP it sends from, each with reverse DNS (PTR) naming a host name that resolves back to it;
- a domain for return paths, whose MX points at this host, so bounces reach it;
- for each domain mail is sent as, SPF, DKIM and DMARC records, which
nitromta doctorwrites out for you; - a synchronised clock (NTP), and, for DANE, a resolver that validates DNSSEC, such as a local unbound.
To try the engine first, without any of this, use capture mode.
1. Install the release
On Debian or Ubuntu, and on RHEL or Fedora, install the release's package, after checking its build provenance, signed by GitHub:
gh attestation verify nitromta_<version>_amd64.deb --repo nitrosend/nitromta
sudo apt install ./nitromta_<version>_amd64.debgh attestation verify nitromta-<version>-1.x86_64.rpm --repo nitrosend/nitromta
sudo dnf install ./nitromta-<version>-1.x86_64.rpmOn arm64 they are nitromta_<version>_arm64.deb and
nitromta-<version>-1.aarch64.rpm. On Arch, nitromta-bin from the AUR. The
package installs the binary, the systemd unit and the engine's system user,
and starts nothing. Then go on to step 2.
On any other Linux, each release also publishes an archive for each
architecture,
nitromta-<version>-x86_64-linux.tar.gz and
nitromta-<version>-aarch64-linux.tar.gz, with build provenance signed by
GitHub. Check it before installing:
gh attestation verify nitromta-<version>-x86_64-linux.tar.gz --repo nitrosend/nitromta
tar -xzf nitromta-<version>-x86_64-linux.tar.gz
cd nitromta-<version>-x86_64-linux
sudo install -m 0755 nitromta /usr/bin/nitromta
sudo install -m 0644 nitromta.service /usr/lib/systemd/system/nitromta.service
sudo install -m 0644 nitromta.sysusers /usr/lib/sysusers.d/nitromta.conf
sudo systemd-sysusers
sudo systemctl daemon-reloadThe unit runs the engine as its own user, nitromta, with
CAP_NET_BIND_SERVICE alone, so it can listen on ports below 1024. The
engine refuses to start as root or with any other capability. It keeps its
store in /var/lib/nitromta, its admin socket in /run/nitromta, and reads
its configuration, certificates and keys from /etc/nitromta.
Or run the container image
Each release publishes an image for both architectures,
ghcr.io/nitrosend/nitromta:<version>, built from the same binaries, with
its provenance signed by GitHub. It holds the binary alone, on glibc and the
certificate authorities, and runs as its own user, uid 65532, with its store
in /var/lib/nitromta and its configuration in /etc/nitromta, each a
volume of its own. The steps below are the same, run in it:
gh attestation verify oci://ghcr.io/nitrosend/nitromta:<version> --repo nitrosend/nitromta
docker volume create nitromta-etc
docker volume create nitromta-store
docker run --rm -v nitromta-etc:/etc/nitromta ghcr.io/nitrosend/nitromta:<version> \
setup --config /etc/nitromta/config.toml \
--hostname mta.example.com --return-path-domain bounces.example.com
docker run -d --name nitromta -p 25:25 -p 443:443 -p 587:587 \
-v nitromta-etc:/etc/nitromta -v nitromta-store:/var/lib/nitromta \
ghcr.io/nitrosend/nitromta:<version>In Docker's own network the engine listens on the usual ports as its own
user, and its mail leaves from the host's address, so the image suits a
host that sends from one address: give it no egresses, and it sends as the
host. A host with an address for each egress installs the package above.
The admin commands run in the container:
docker exec -i nitromta nitromta admin --config /etc/nitromta/config.toml.
2. Write the configuration
As the engine's user, so everything it writes is that user's:
sudo install -d -o nitromta -g nitromta /etc/nitromta
sudo -u nitromta nitromta setup --config /etc/nitromta/config.toml \
--hostname mta.example.com --return-path-domain bounces.example.comsetup writes a commented configuration, with every other setting at its
default: submission on 587, bounces and complaints taken on 25 for the
return path domain ([feedback]), and delivery only over TLS ([delivery] require_tls). It writes the installation's keys too, which seal the secrets
the store keeps. It never writes over a file already there. Keep a copy of
the configuration and the keys away from the host: a backup of the store can
be restored only with them.
3. Give it a certificate
Submission, the SES API and the feedback listener serve TLS with the
certificate at /etc/nitromta/tls.crt and its key at
/etc/nitromta/tls.key, which must be for the host name given to setup,
from a certificate authority your clients trust. A renewal's hook runs
systemctl reload nitromta, and the engine reads the new certificate
without a restart.
The SES API is off until its [ses] table is uncommented. It listens on
443 by default.
4. Start it
sudo systemctl enable --now nitromtaA stop waits for attempts under way, up to [scheduler] drain_seconds (20
by default).
5. Give it its policy
Through the admin socket, with one JSON line for each change. An account, an egress for each address mail goes out from, an identity for each domain mail is sent as, with its DKIM key, and a login for an SMTP client:
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out dkim.pem
key=$(jq -Rs . dkim.pem)
sudo -u nitromta nitromta admin --config /etc/nitromta/config.toml <<END
{"op":"put_account","account":1}
{"op":"put_egress","account":1,"address":"203.0.113.10","helo":"mta.example.com"}
{"op":"put_identity","account":1,"name":"example.com","selector":"s1","private_key":$key}
{"op":"create_login","account":1,"user":"app"}
ENDAnd an access key for an application that calls the SES API:
sudo -u nitromta nitromta access-key create --config /etc/nitromta/config.toml --account 1An egress may take a daily cap at each receiver, and warms up from the receiver rules' ladder; operations has the rest of policy: pools, configurations, tenants, holds and subscriptions.
6. Check it
sudo -u nitromta nitromta doctor --config /etc/nitromta/config.tomldoctor checks the installation with what the engine itself uses: its
configuration, store and certificates, its clock, its resolver, and,
through the running engine, each egress's reverse DNS, its blocklist
listings and the return path's SPF record covering it, and each identity's
DKIM and DMARC records. Each check prints
ok, warn or fail with what it found, and a failure says what to
change: for a missing SPF, DKIM or DMARC record, the exact record to
publish. It changes nothing.
A record just published may not show at once: a resolver keeps the answer
it had, even that there was none, for as long as that answer's TTL, so
doctor can report a record missing for a while after it went in. A
changed PTR takes hours to reach every receiver's resolver, which until
then see the provider's name for the address: publish the records, the PTR
first, a day before the first mail goes out.
An identity put through the admin socket is the operator's word, and sends
at once; doctor is how it is checked. One an application creates through
the SES API is checked as SES checks it, by its DKIM records, and mail sent
as it is refused until they check out.
Upgrading
Stop the engine, install the new binary and start it again. An upgrade
keeps the store: a release that changes it brings it up to date as it
opens, one step in one transaction at a time, so a step cut short leaves it
as it was. nitromta migrate --config /etc/nitromta/config.toml does the
same without starting the engine, and says from which version to which. A
store written by a newer release is refused, never read, so going back to
an older release means restoring a backup taken before the upgrade.
Before 1.0, a minor release (0.1 to 0.2) may change a configuration key, a command or an admin operation; its changelog says exactly what to edit. A patch release (0.1.0 to 0.1.1) changes none of them.