What you will set up
A mail server for your own domain, made of three programs and a certificate:
- Postfix sends and receives mail,
- Dovecot lets your mail app read it over IMAP, and checks passwords when you send,
- OpenDKIM signs every message you send, so receivers can verify it came from you,
- a Let's Encrypt certificate encrypts all of it.
Every step below was run on a fresh Melonslab server with Debian 13. A test message sent through it passed SPF, reverse DNS and DKIM at an external verifier, the verifier's reply arrived in the mailbox, and Gmail accepted a connection over IPv6.
If you would rather have one program with spam filtering and a web interface for managing domains and mailboxes, see the Stalwart guide.
Before you start
You need:
- a server with Debian 13 and root access,
- an IPv4 address on the server (many mail servers still accept mail over IPv4 only), and
- a domain whose DNS records you can change.
The examples use example.com for your domain, mail.example.com for the server, 203.0.113.10 for its IPv4 address and 2001:db8:1f::a for its IPv6 address. Replace them with your own throughout.
Port 25 is open on every Melonslab server from the first boot, so there is nothing to request.
1. Point DNS at your server
At your DNS provider, create these records:
| Name | Type | Value |
|---|---|---|
mail.example.com | A | 203.0.113.10 |
mail.example.com | AAAA | 2001:db8:1f::a |
example.com | MX | mail.example.com, priority 10 |
The MX record tells other mail servers to deliver mail for example.com to mail.example.com.
2. Set reverse DNS
Receiving servers look up the name behind your server's address and check that it points back to the same address. In the Melonslab panel, open your server's network tab and set the reverse DNS of both addresses to mail.example.com. The panel accepts it once the A and AAAA records from step 1 exist.
Check it from the server (bind9-dnsutils provides dig):
apt install -y bind9-dnsutils
dig +short -x 203.0.113.10
dig +short -x 2001:db8:1f::a
Both should print mail.example.com.. Set both: when the receiving server has IPv6, Postfix may deliver over either protocol, and Gmail, for one, rejects mail from an IPv6 address without a matching name.
3. Set the hostname
hostnamectl set-hostname mail.example.com
sed -i "1i 203.0.113.10 mail.example.com mail" /etc/hosts
4. Install the software
The first two lines answer the Postfix installer's questions in advance:
echo "postfix postfix/main_mailer_type select Internet Site" | debconf-set-selections
echo "postfix postfix/mailname string example.com" | debconf-set-selections
apt update
apt install -y postfix dovecot-imapd opendkim opendkim-tools certbot
5. Get a certificate
certbot certonly --standalone -d mail.example.com --agree-tos -m you@example.com
certbot answers the certificate check on port 80 itself and renews the certificate automatically. Postfix and Dovecot read it at startup, so have them reload it after every renewal:
mkdir -p /etc/letsencrypt/renewal-hooks/deploy
printf '#!/bin/sh\nsystemctl reload postfix dovecot\n' > /etc/letsencrypt/renewal-hooks/deploy/reload-mail
chmod 755 /etc/letsencrypt/renewal-hooks/deploy/reload-mail
6. Configure Postfix
Use the certificate, deliver mail into each user's ~/Maildir, and let Dovecot check passwords:
postconf -e \
"smtpd_tls_cert_file = /etc/letsencrypt/live/mail.example.com/fullchain.pem" \
"smtpd_tls_key_file = /etc/letsencrypt/live/mail.example.com/privkey.pem" \
"smtpd_tls_security_level = may" \
"smtp_tls_security_level = may" \
"home_mailbox = Maildir/" \
"smtpd_sasl_type = dovecot" \
"smtpd_sasl_path = private/auth"
Turn on port 587, where your mail app sends from. It requires encryption and a password:
postconf -M "submission/inet=submission inet n - y - - smtpd"
postconf -P "submission/inet/syslog_name=postfix/submission" \
"submission/inet/smtpd_tls_security_level=encrypt" \
"submission/inet/smtpd_sasl_auth_enable=yes" \
"submission/inet/smtpd_client_restrictions=permit_sasl_authenticated,reject" \
"submission/inet/smtpd_relay_restrictions=permit_sasl_authenticated,reject"
postfix check
Port 25 keeps Debian's default rules: it accepts mail for your domain from anyone, and relays elsewhere only for the server itself. That is what stops it from becoming an open relay.
7. Configure Dovecot
Debian 13 ships Dovecot 2.4, whose settings differ from the 2.3 examples you will find in most places. Put yours in /etc/dovecot/local.conf, which Dovecot reads last:
# Mail in ~/Maildir, the same place Postfix delivers to
mail_driver = maildir
mail_path = ~/Maildir
mail_inbox_path =
ssl_server_cert_file = /etc/letsencrypt/live/mail.example.com/fullchain.pem
ssl_server_key_file = /etc/letsencrypt/live/mail.example.com/privkey.pem
# Postfix asks Dovecot to check passwords for mail sent on port 587
service auth {
unix_listener /var/spool/postfix/private/auth {
mode = 0660
user = postfix
group = postfix
}
}
Check it:
doveconf -n > /dev/null && echo valid
8. Sign mail with DKIM
Create a key, and point OpenDKIM at it:
opendkim-genkey -b 2048 -d example.com -s mail -D /etc/dkimkeys
chown opendkim:opendkim /etc/dkimkeys/mail.private
sed -i "s|^Socket\s.*|Socket\t\t\tinet:8891@localhost|" /etc/opendkim.conf
cat >> /etc/opendkim.conf <<EOF
Domain example.com
Selector mail
KeyFile /etc/dkimkeys/mail.private
EOF
OpenDKIM listens on a local port rather than its default socket file, because Postfix runs in a restricted directory from which it cannot reach that file. Tell Postfix to pass every message through it:
postconf -e 'smtpd_milters = inet:localhost:8891' 'non_smtpd_milters = $smtpd_milters' 'milter_default_action = accept'
systemctl restart opendkim
The public key to publish is in /etc/dkimkeys/mail.txt:
cat /etc/dkimkeys/mail.txt
Create a TXT record named mail._domainkey.example.com holding the text between the quotes, joined into one string: v=DKIM1; h=sha256; k=rsa; p=MIIBIjANBg.... It is longer than 255 characters; most DNS providers split it for you. Then check it:
opendkim-testkey -d example.com -s mail -vvv
It should end with key OK.
9. Publish SPF and DMARC
Two more TXT records:
| Name | Type | Value |
|---|---|---|
example.com | TXT | v=spf1 mx -all |
_dmarc.example.com | TXT | v=DMARC1; p=none; rua=mailto:postmaster@example.com |
SPF says that only your MX server sends mail for your domain. DMARC tells receivers what to do with mail that fails SPF and DKIM, and where to send reports. Start with p=none, read the reports for a few weeks, then move to p=quarantine.
10. Start everything and create a mailbox
systemctl restart postfix dovecot
useradd -m -s /usr/sbin/nologin anna
passwd anna
That creates the mailbox anna@example.com. The user cannot log in to the server itself, only read and send mail.
11. Check that it works
From the server, send a message to a verifier that checks it and replies with a report. swaks is a small test tool:
apt install -y swaks
swaks --server=mail.example.com --port=587 --tls --auth=PLAIN \
--auth-user=anna --auth-password='YOUR_PASSWORD' \
--from=anna@example.com --to=check-auth@verifier.port25.com
The report arrives in Anna's mailbox within a minute and should show SPF check: pass, "iprev" check: pass and DKIM check: pass. Receiving it also shows that mail from outside reaches your server.
Connect your mail app
| Server | Port | Security | |
|---|---|---|---|
| Incoming (IMAP) | mail.example.com | 993 | SSL/TLS |
| Outgoing (SMTP) | mail.example.com | 587 | STARTTLS |
The username is the mailbox's user name, anna, and the password the one you set.
Troubleshooting
Your mail app says the login method is not supported. Dovecot offers the PLAIN method, over the encrypted connection. Choose "Normal password" or PLAIN in the app's settings.
Mail does not arrive, or is rejected. Postfix logs every delivery:
journalctl -t postfix/smtp -t postfix/smtpd -t postfix/submission/smtpd -n 50
A rejection that mentions reverse DNS or PTR means step 2 is not done for the address Postfix used; check both.
DKIM fails. Run opendkim-testkey from step 8 again, and check that systemctl status opendkim is running.
If you use ufw as in the security guide, allow ports 25, 587 and 993, and port 80 for certificate renewal: ufw allow 25,80,587,993/tcp.
Sending in bulk?
These steps are for your own mail, your company's mail and transactional mail from your applications. Newsletters and other high-volume sending need an arrangement with our sales team first, so we can protect the reputation of the addresses every customer relies on.