GuidesChat and emailMail server with Postfix

Run your own mail server on Debian

A mail server for your own domain on Debian 13, with Postfix to send and receive, Dovecot for your mail apps, and DKIM, SPF and DMARC, so your mail passes the checks the big mailbox providers run.

Tested on Debian 13 (trixie) on a Melonslab server Updated September 25, 2026

Recommended server for this guide

VC-S Micro · 2 vCPU · 8 GB Memory · 250 GB Storage

Month to month, no lock-in 7-day money-back guarantee

€7.99/mo

Deploy now
On this page

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:

NameTypeValue
mail.example.comA203.0.113.10
mail.example.comAAAA2001:db8:1f::a
example.comMXmail.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:

NameTypeValue
example.comTXTv=spf1 mx -all
_dmarc.example.comTXTv=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

ServerPortSecurity
Incoming (IMAP)mail.example.com993SSL/TLS
Outgoing (SMTP)mail.example.com587STARTTLS

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.

Run it on your own server

VC-S Micro

€7.99/mo

vCPU
2
Memory
8 GB
Storage
250 GB
Transfer
10 TB
Standard
HDD · RAID 10
  • Full root access
  • Native /64 IPv6
  • RAID-protected storage
  • Malmö, Sweden
  • Month to month, no lock-in
  • 7-day money-back guarantee
All guides