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.1 to point to mail.example.com.

  • Configure 2001:db8::1 to point to mail.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., so 192.0.2.1 becomes 1.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.