From 4089d73b51b01160e2e9c937ec39426dbb7aff2f Mon Sep 17 00:00:00 2001 From: Martin Weinelt Date: Mon, 9 Mar 2026 03:58:35 +0100 Subject: [PATCH] docs/setup-{guide,example}: refresh the whole guide - add many motivation, helpful comments and important details - improve formatting through use of more native sphinx/rst elements, like the csv-table for DNS records - clarify the basic requirements - use dig for uncached DNS propagation checks against an authoritative nameserver - explain the basic feature set of the setup example - adjust DNS TTLs; 1h is a common duration in modern setups and does not hurt caching much - remove mention of the announce mailinglist, users can just expect releases to be ready around branch-off --- docs/setup-example.nix | 19 ++- docs/setup-guide.rst | 361 ++++++++++++++++++++++++++--------------- 2 files changed, 244 insertions(+), 136 deletions(-) diff --git a/docs/setup-example.nix b/docs/setup-example.nix index e25f0b1..ea0dc50 100644 --- a/docs/setup-example.nix +++ b/docs/setup-example.nix @@ -5,10 +5,15 @@ { imports = [ (builtins.fetchTarball { - # Pick a release version you are interested in and set its hash, e.g. + # 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-25.11/nixos-mailserver-nixos-25.11.tar.gz"; - # To get the sha256 of the nixos-mailserver tarball, we can use the nix-prefetch-url command: - # release="nixos-25.11"; nix-prefetch-url "https://gitlab.com/simple-nixos-mailserver/nixos-mailserver/-/archive/${release}/nixos-mailserver-${release}.tar.gz" --unpack + + # Hash of the unpacked tarball, run the following command to retrieve it + # release="nixos-25.11" nix-prefetch-url "https://gitlab.com/simple-nixos-mailserver/nixos-mailserver/-/archive/${release}/nixos-mailserver-${release}.tar.gz" --unpack sha256 = "0000000000000000000000000000000000000000000000000000"; }) ]; @@ -30,18 +35,22 @@ fqdn = "mail.example.com"; domains = [ "example.com" ]; - # reference an existing ACME configuration + # 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' loginAccounts = { "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"; }; }; }; diff --git a/docs/setup-guide.rst b/docs/setup-guide.rst index 1f99c96..02f0d0a 100644 --- a/docs/setup-guide.rst +++ b/docs/setup-guide.rst @@ -5,207 +5,306 @@ 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 ``_. -What you need is: +Requirements +~~~~~~~~~~~~ -- a server running NixOS with a public IP -- a domain name. +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 hoster make you to 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:: - In the following, we consider a server with the public IP ``1.2.3.4`` - and the domain ``example.com``. + 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. -First, we will set the minimum DNS configuration to be able to deploy -an up and running mail server. Once the server is deployed, we could -then set all DNS entries required to send and receive mails on this -server. +Configure forward DNS records +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Setup DNS A/AAAA records for server -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +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. -Add DNS records to the domain ``example.com`` with the following -entries +Now edit the ``example.com`` zone and create the following DNS records: -==================== ===== ==== ============= -Name (Subdomain) TTL Type Value -==================== ===== ==== ============= -``mail.example.com`` 10800 A ``1.2.3.4`` -``mail.example.com`` 10800 AAAA ``2001::1`` -==================== ===== ==== ============= +.. csv-table:: + :header: "Name", "TTL", "Type", "Value" + :widths: 30, 10, 10, 50 -If your server does not have an IPv6 address, you must skip the `AAAA` record. + mail.example.com., 3600, A, 192.0.2.1 + mail.example.com., 3600, AAAA, 2001:db8::1 -You can check this with +.. note:: + If your server does not have an IPv6 address, you must skip the ``AAAA`` + record. -:: +Verify DNS record propagation +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - $ nix-shell -p bind --command "host -t A mail.example.com" - mail.example.com has address 1.2.3.4 +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. - $ nix-shell -p bind --command "host -t AAAA mail.example.com" - mail.example.com has address 2001::1 +.. code-block:: console -Note that it can take a while until a DNS entry is propagated. This -DNS entry is required for the Let's Encrypt certificate generation -(which is used in the below configuration example). + # 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 describes a server setup that is fairly complete. Even -though there are more possible options (see the `NixOS Mailserver -options documentation `_), these should be the most -common ones. +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. + +.. _more options: options.html .. literalinclude:: ./setup-example.nix :language: nix -After a ``nixos-rebuild switch`` your server should be running all -mail components. +After a ``nixos-rebuild switch`` your server should be running all the necessary +mail services. -Setup all other DNS requirements -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Configure DNS records +~~~~~~~~~~~~~~~~~~~~~ -Set rDNS (reverse DNS) entry for server -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Reverse DNS +^^^^^^^^^^^ -Wherever you have rented your server, you should be able to set reverse -DNS entries for the IP’s you own: +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. -- Add an entry resolving IPv4 address ``1.2.3.4`` to ``mail.example.com``. -- Add an entry resolving IPv6 ``2001::1`` to ``mail.example.com``. Again, this - must be skipped if your server does not have an IPv6 address. +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: + +.. csv-table:: + :header: "Name", "TTL", "Type", "Value" + :widths: 30, 10, 10, 50 + + 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.`` + + .. code-block:: console + + 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 not able to - set a reverse DNS on your public IP because sent emails would be - mostly marked as spam. Note that many residential ISP providers - don't allow you to set a reverse DNS entry. + 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. -You can check this with +DNS propagation often isn't instant, so verify before continuing: -:: +.. code-block:: console - $ nix-shell -p bind --command "host 1.2.3.4" - 4.3.2.1.in-addr.arpa domain name pointer mail.example.com. + $ nix-shell -p dig --command "dig -x 192.0.2.1 +short" + mail.example.com. - $ nix-shell -p bind --command "host 2001::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.0.0.0.0.1.0.0.2.ip6.arpa domain name pointer mail.example.com. - -Note that it can take a while until a DNS entry is propagated. - -Set a ``MX`` record -^^^^^^^^^^^^^^^^^^^ + $ nix-shell -p dig --command "dig -x 2001:db8::1 +short" + mail.example.com. -Add a ``MX`` record to the domain ``example.com``. +MX record +^^^^^^^^^ -================ ==== ======== ================= -Name (Subdomain) Type Priority Value -================ ==== ======== ================= -example.com MX 10 mail.example.com -================ ==== ======== ================= +The MX record instructs other mailservers where to deliver mail for a domain +name. -You can check this with +Create the MX record for ``example.com`` to point to the hostname of the server. -:: +.. csv-table:: + :header: "Name", "TTL", "Priority", "Type", "Value" + :widths: 30, 10, 10, 10, 50 - $ nix-shell -p bind --command "host -t mx example.com" - example.com mail is handled by 10 mail.example.com. + example.com., 3600, MX, 10, mail.example.com. -Note that it can take a while until a DNS entry is propagated. +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. -Set a ``SPF`` record -^^^^^^^^^^^^^^^^^^^^ +.. code-block:: console -Add a `SPF `_ -record to the domain ``example.com``. + $ nix-shell -p dig --command "dig @ns1.example.org MX example.com +short" + 10 mail.example.com. -================ ===== ==== ================ -Name (Subdomain) TTL Type Value -================ ===== ==== ================ -example.com 10800 TXT `v=spf1 mx -all` -================ ===== ==== ================ +SPF record +^^^^^^^^^^ -You can check this with +With `SPF`_ we can specify which mail servers are authorized to send mail on +behalf of a domain name. -:: +.. _SPF: https://en.wikipedia.org/wiki/Sender_Policy_Framework - $ nix-shell -p bind --command "host -t TXT example.com" - example.com descriptive text "v=spf1 mx -all" +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. -Note that it can take a while until a DNS entry is propagated. +.. csv-table:: + :header: "Name", "TTL", "Type", "Value" + :widths: 30, 10, 10, 50 -Set ``DKIM`` signature -^^^^^^^^^^^^^^^^^^^^^^ + example.com., 86400, TXT, v=spf1 mx -all -On your server, the ``rspamd`` systemd service generated a file -containing your DKIM public key in the file -``/var/dkim/example.com.mail.txt``. The content of this file looks -like +.. code-block:: console -:: + $ nix-shell -p dig --command "dig TXT example.com" + v=spf1 mx -all - mail._domainkey IN TXT ( "v=DKIM1; k=rsa; " - "p=" ) ; ----- DKIM key mail for nixos.org -where ``really-long-key`` is your public key. +DKIM record +^^^^^^^^^^^ -Based on the content of this file, we can add a ``DKIM`` record to the -domain ``example.com``. +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. -=========================== ===== ==== ======================================= -Name (Subdomain) TTL Type Value -=========================== ===== ==== ======================================= -mail._domainkey.example.com 10800 TXT ``v=DKIM1; k=rsa; p=`` -=========================== ===== ==== ======================================= +.. _DKIM: https://en.wikipedia.org/wiki/DomainKeys_Identified_Mail -You can check this with +Now, check ``/var/dkim/example.com.mail.txt``, which contains the proposed DNS +record for the ``mail`` DKIM selector. -:: +.. code-block:: none - $ nix-shell -p bind --command "host -t txt mail._domainkey.example.com" - mail._domainkey.example.com descriptive text "v=DKIM1;p=" + mail._domainkey IN TXT ( "v=DKIM1; k=rsa; " + "p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv7hSess/UgEjaaq/NDn5KtW2iZzYljhf45DH3tN/kqcJ04JJk/Z1rS7CMJQ/pYZSSnQOju0H25uOtODvhqXPDxDdtCyDSrx54z/38lGNtA76/iWy/ikjb9hEkb2k3HuKex3P4KhhOC1pytDEFnh/T2aBxPNOigc/cpqm1U9RbnAwvArtx9dgOAgiV8rOIgPgyrPw1B3cJG3hgFYU2" + "GwXMoiFQPgwm7bkjelmThqXozA7jFJfnYt49jjrIYCv8X/nQx9cNpVAv2852mhU/3uuy6sa4MPjT6RiK9BJCMyDnqSpTPCjIubL4VhGCuzp7RPBkayWnlaH0X8PWGq6BQ0eBwIDAQAB" + ) ; -Note that it can take a while until a DNS entry is propagated. +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. -Set a ``DMARC`` record -^^^^^^^^^^^^^^^^^^^^^^ +.. csv-table:: + :header: "Name", "TTL", "Type", "Value" + :widths: 30, 10, 10, 50 -Add a ``DMARC`` record to the domain ``example.com``. + example.com., 86400, TXT, v=DKIM1; k=rsa; p=MIIBIjANBgk...Q0eBwIDAQAB -======================== ===== ==== ==================== -Name (Subdomain) TTL Type Value -======================== ===== ==== ==================== -_dmarc.example.com 10800 TXT ``v=DMARC1; p=none`` -======================== ===== ==== ==================== +.. code-block:: console -You can check this with + $ nix-shell -p dig --command "dig @ns1.example.org TXT mail._domainkey.example.com +short" + "v=DKIM1; k=rsa; p=MIIBIjANBgk...Q0eBwIDAQAB" -:: - $ nix-shell -p bind --command "host -t TXT _dmarc.example.com" - _dmarc.example.com descriptive text "v=DMARC1; p=none" +DMARC record +^^^^^^^^^^^^ -Note that it can take a while until a DNS entry is propagated. +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. + +.. csv-table:: + :header: "Name", "TTL", "Type", "Value" + :widths: 30, 10, 10, 50 + + _dmarc.example.com., 86400, TXT, v=DMARC1; p=none; + +Verify propagation one final time. + +.. code-block:: console + + $ nix-shell -p dig --command "dig @ns1.example.org TXT _dmarc.example.com" + "v=DMARC1; p=none" Test your Setup ~~~~~~~~~~~~~~~ -Write an email to your aunt (who has been waiting for your reply far too -long), and sign up for some of the finest newsletters the Internet has. -Maybe you want to sign up for the `SNM Announcement -List `__? +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. -Besides that, you can send an email to -`mail-tester.com `__ and see how you -score, and let `mxtoolbox.com `__ take a look at -your setup, but if you followed the steps closely then everything should -be awesome! +You can also let `MXToolbox`_ take a peek at your setup. If you followed the +steps carefully, everything should be working perfectly! -Next steps (optional) -~~~~~~~~~~~~~~~~~~~~~ +.. _mail-tester.com: https://mail-tester.com/ +.. _MXToolbox: https://mxtoolbox.com/ -Take a look through our `Advanced Configurations `_. + +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. + +.. _#nixos-mailserver:nixos.org: https://matrix.to/#/#nixos-mailserver:nixos.org + +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 Governement) +- `MECSA`_ (supported by the European Commission) + +Finally, you can also browse the full list of `options`_ provided by NixOS mailserver. + +.. _advanced configurations: advanced-configurations.html +.. _options: options.html +.. _internet.nl: https://internet.nl/test-mail/ +.. _MECSA: https://mecsa.jrc.ec.europa.eu/