Setup Guide
Mail servers can be a tricky thing to set up. This guide is supposed to run you through the most important steps to achieve a 10/10 score on https://mail-tester.com.
Requirements
To set up a self-hosted mail server, you need the following:
Small (e.g. 1C/2G) server running NixOS
Stable IPv4 and - strongly recommended - IPv6 addresses
Ability to configure a Reverse DNS (PTR record) for your IP addresses
Access to SMTP traffic on port 25/tcp - some hosters make you ask for this
A registered domain name with DNS record management access
Once these requirements are in place, you can begin setting up your selfhosted mailserver.
Note
Below we'll assume that your server got assigned the public IP addresses
192.0.2.1 (IPv4) and 2001:db8::1 (IPv6) and that you control the
example.com domain.
Configure forward DNS records
Here we set up mail.example.com as the forward hostname for your mail server
to point to the IP addresses allocated to the server. This allows reaching
the server under this name and to reference it later in MX records for mail
delivery.
Now edit the example.com zone and create the following DNS records:
Name |
TTL |
Type |
Value |
|---|---|---|---|
mail.example.com. |
3600 |
A |
192.0.2.1 |
mail.example.com. |
3600 |
AAAA |
2001:db8::1 |
Note
If your server does not have an IPv6 address, you must skip the AAAA
record.
Verify DNS record propagation
Before we continue with the next step, we require that the forward DNS record
has propagated. For that it's best to check an authoritative nameserver for
example.com so that we don't look at cached DNS records.
# Find the authoritative nameservers for example.com
$ nix-shell -p dig --command "dig NS example.com +short"
ns1.example.org.
ns2.example.org.
# Query the A record from an authoritative nameserver
$ nix-shell -p dig --command "dig @ns1.example.org A mail.example.com +short"
192.0.2.1
# Query the AAAA record from an authoritative nameserver
$ nix-shell -p dig --command "dig @ns1.example.org AAAA mail.example.com +short"
2001:db8::1
DNS propagation usually takes a few minutes, so you might need to retry these queries. Once the IP addresses appear you can continue with the next step.
Setup the server
The following configuration describes a fairly complete mail server, capable of sending and receiving mail for statically configured accounts. It includes encrypted SMTP and IMAP services for secure delivery and retrieval, and relies on ACME HTTP-01 to automatically obtain and maintain a TLS certificate.
While more options are available, the configuration below covers the most common settings to get your mail server up and running.
{
config,
...
}:
{
imports = [
(builtins.fetchTarball {
# This is a quick and dirty way to import a NixOS mailserver release. What
# you should do long-term is use a proper dependency pinning tool like npins
# or flakes.
# URL to the tarball for the release matching your NixOS release
url = "https://gitlab.com/simple-nixos-mailserver/nixos-mailserver/-/archive/nixos-26.05/nixos-mailserver-nixos-26.05.tar.gz";
# Hash of the unpacked tarball, run the following command to retrieve it
# release="nixos-26.05" nix-prefetch-url "https://gitlab.com/simple-nixos-mailserver/nixos-mailserver/-/archive/${release}/nixos-mailserver-${release}.tar.gz" --unpack
sha256 = "0000000000000000000000000000000000000000000000000000";
})
];
# https://letsencrypt.org/repository/#let-s-encrypt-subscriber-agreement
security.acme.acceptTerms = true;
# Allow incoming HTTP connections
networking.firewall.allowedTCPPorts = [ 80 ];
# Enable ACME HTTP-01 challenge with nginx
services.nginx = {
enable = true;
virtualHosts.${config.mailserver.fqdn}.enableACME = true;
};
mailserver = {
enable = true;
stateVersion = 5;
fqdn = "mail.example.com";
domains = [ "example.com" ];
# Reference the existing ACME configuration created by nginx
x509.useACMEHost = config.mailserver.fqdn;
# A list of all login accounts. To create the password hashes, use
# nix-shell -p mkpasswd --run 'mkpasswd -s'
accounts = {
"user1@example.com" = {
# Reads the password hash from a file on the server
hashedPasswordFile = "/a/file/containing/a/hashed/password";
# Additional addresses delivered to this mailbox
aliases = [ "postmaster@example.com" ];
};
"user2@example.com" = {
# Provides the password hash inline
hashedPassword = "$y$j9T$JqqefR6flaaJBRjD4KVZc1$QM6h4Spr5.yn/FuIT.ydTV22daEbiVd8ZprV/POtPgB";
};
};
};
}
After a nixos-rebuild switch your server should be running all the necessary
mail services.
Configure DNS records
Reverse DNS
Earlier, we configured forward DNS from your hostname to your IP address. Now we will configure reverse DNS so that your IP address points back to your hostname.
If your forward and reverse DNS do not match, many mail servers will reject or flag your emails as spam, severely impairing delivery.
Your server provider should allow you to configure reverse DNS (PTR record) records for the IP addresses you control, typically through their control panel or account management interface:
Configure
192.0.2.1to point tomail.example.com.Configure
2001:db8::1to point tomail.example.com., if you have IPv6 addressing
Alternatively, if you interact with a proper DNS setup, the actual DNS records look like this:
Name |
TTL |
Type |
Value |
|---|---|---|---|
1.2.0.192.in-addr.arpa. |
86400 |
PTR |
mail.example.com. |
1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa. |
86400 |
PTR |
mail.example.com. |
Note
Reverse DNS uses reverse notation for naming its records:
IPv4 reverses the order of the octets and appends
in-addr.arpa., so192.0.2.1becomes1.2.0.192.in-addr.arpa.IPv6 fully expands the address and reverses each hex digit before concatenating it with dots and appending
ip6.arpa.
nix-shell -p haskellPackages.ip6addr --command "ip6addr --ptr 2001:db8::1"
1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.IP6.ARPA.
Warning
We don't recommend setting up a mail server if you are unable to configure reverse DNS on your public IP addresses because mails would inevitable be marked as spam. Note that many residential ISP providers don't allow you to set a reverse DNS entry and prohibit sending mail through policy blocklists like Spamhaus PBL.
DNS propagation often isn't instant, so verify before continuing:
$ nix-shell -p dig --command "dig -x 192.0.2.1 +short"
mail.example.com.
$ nix-shell -p dig --command "dig -x 2001:db8::1 +short"
mail.example.com.
MX record
The MX record instructs other mailservers where to deliver mail for a domain name.
Create the MX record for example.com to point to the hostname of the server.
Name |
TTL |
Priority |
Type |
Value |
|---|---|---|---|---|
example.com. |
3600 |
MX |
10 |
mail.example.com. |
The priority field determines the order when multiple servers are configured. It is not important in this scenario but setting a value is mandatory and 10 leaves some wiggle room below and above, should you ever need that.
$ nix-shell -p dig --command "dig @ns1.example.org MX example.com +short"
10 mail.example.com.
SPF record
With SPF we can specify which mail servers are authorized to send mail on behalf of a domain name.
The SPF record is TXT record and we can tie it in with the MX record we created
in the previous step. Finishing with -all indicates that without any match
the mail should be rejected.
Name |
TTL |
Type |
Value |
|---|---|---|---|
example.com. |
86400 |
TXT |
v=spf1 mx -all |
$ nix-shell -p dig --command "dig TXT example.com +short"
v=spf1 mx -all
DKIM record
On system activation a DKIM keypair for example.com was generated. The
mail server uses this key to sign outgoing emails, allowing receiving servers to
verify the authenticity of the sender domain and ensuring that the signed parts
of the message have not been tampered with.
Now, check /var/dkim/example.com.mail.txt, which contains the proposed DNS
record for the mail DKIM selector.
mail._domainkey IN TXT ( "v=DKIM1; k=rsa; "
"p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv7hSess/UgEjaaq/NDn5KtW2iZzYljhf45DH3tN/kqcJ04JJk/Z1rS7CMJQ/pYZSSnQOju0H25uOtODvhqXPDxDdtCyDSrx54z/38lGNtA76/iWy/ikjb9hEkb2k3HuKex3P4KhhOC1pytDEFnh/T2aBxPNOigc/cpqm1U9RbnAwvArtx9dgOAgiV8rOIgPgyrPw1B3cJG3hgFYU2"
"GwXMoiFQPgwm7bkjelmThqXozA7jFJfnYt49jjrIYCv8X/nQx9cNpVAv2852mhU/3uuy6sa4MPjT6RiK9BJCMyDnqSpTPCjIubL4VhGCuzp7RPBkayWnlaH0X8PWGq6BQ0eBwIDAQAB"
) ;
Based on the content of this file, we can create the DKIM TXT record for the
mail selector in the example.com zone. For the p= value, glue the
two long strings back together without any quotes and spaces and put them into
your record below.
Name |
TTL |
Type |
Value |
|---|---|---|---|
mail._domainkey.example.com. |
86400 |
TXT |
v=DKIM1; k=rsa; p=MIIBIjANBgk...Q0eBwIDAQAB |
$ nix-shell -p dig --command "dig @ns1.example.org TXT mail._domainkey.example.com +short"
"v=DKIM1; k=rsa; p=MIIBIjANBgk...Q0eBwIDAQAB"
DMARC record
Finally, DMARC lets you define a policy for how strictly SPF and DKIM should be checked and how to handle validation failures. For a new server, it’s important to have a DMARC record in place, even if it doesn’t enforce any actions yet, because it improves deliverability by showing receiving servers that your domain is properly managed and reducing the risk of email spoofing.
Name |
TTL |
Type |
Value |
|---|---|---|---|
_dmarc.example.com. |
86400 |
TXT |
v=DMARC1; p=none; |
Verify propagation one final time.
$ nix-shell -p dig --command "dig @ns1.example.org TXT _dmarc.example.com +short"
"v=DMARC1; p=none"
Test your Setup
Write an email to your aunt — she’s been waiting far too long for your reply, and this is your chance to finally make her day. Or, if you prefer a less emotional test, send a message to mail-tester.com to see how your outgoing mail scores.
You can also let MXToolbox take a peek at your setup. If you followed the steps carefully, everything should be working perfectly!
Join the community
The community has a lively chat room on Matrix at #nixos-mailserver:nixos.org where you can ask questions, get help, share ideas, or discuss contributions.
Next steps
Your server scored perfect results already, so these steps are entirely optional.
Are you feeling adventurous? Dive into our advanced configurations to explore additional features and capabilities that let you fine-tune and extend your mail setup.
If you want to take things even further, more elaborate testing services can give you a clearer picture of your mail service and suggest ways to improve it.
internet.nl (supported by the Dutch Government)
MECSA (supported by the European Commission)
Finally, you can also browse the full list of options provided by NixOS mailserver.