Merge branch 'setup-guide-next' into 'master'

docs/setup-{guide,example}: refresh the whole guide

See merge request simple-nixos-mailserver/nixos-mailserver!488
This commit is contained in:
Martin Weinelt
2026-03-11 02:04:26 +00:00
2 changed files with 244 additions and 136 deletions
+14 -5
View File
@@ -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";
};
};
};
+230 -131
View File
@@ -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
`<https://mail-tester.com>`_.
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 <options.html>`_), 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 IPs 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 <https://en.wikipedia.org/wiki/Sender_Policy_Framework>`_
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=<really-long-key>" ) ; ----- 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=<really-long-key>``
=========================== ===== ==== =======================================
.. _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=<really-long-key>"
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, its important
to have a DMARC record in place, even if it doesnt 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 <https://www.freelists.org/list/snm>`__?
Write an email to your aunt — shes 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 <https://www.mail-tester.com/>`__ and see how you
score, and let `mxtoolbox.com <http://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 <advanced-configurations.html>`_.
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/