rss logo

Install and Configure a Secure Mail Server on Debian with Postfix and Dovecot

Illustration showing Linux, Postfix, and Dovecot as a superhero team, representing the Fantastic Four of mail servers

In this guide, I’ll show you how to build a complete mail server architecture on GNU/Linux. The objective is to send and receive emails seamlessly from different devices — such as Thunderbird on PC or Mac, and email clients on smartphones.

For this tutorial, we’ll use Debian installed on a Virtual Private Server (VPS) hosted by OVHcloud 🇫🇷. (This is not a sponsored post — though I wouldn’t mind if they sent me a T-shirt 😄).

That said, even if some steps are OVHcloud-specific, you can easily follow this guide with any other VPS provider. Simply adapt the configuration to match your hosting environment.

Before we get started, let’s take a moment to consider the pros and cons of running your own mail server. Among the benefits: you can create unlimited email addresses, manage your own storage, and stay fully independent from external providers and their pricing changes. However, hosting your own mail server also means you’re responsible for security and maintenance. I strongly recommend enabling automatic updates and setting up regular backups (most VPS providers offer backup options for a small fee) — for instance, using rsync. You’ll also need to purchase a domain name and maintain your VPS or self-hosted server.

Now that you know the main advantages and challenges, let’s move on to the installation process.

Here’s what our final architecture will look like:

Diagram showing a Debian VPS mail server using Postfix and Dovecot on OVHcloud with remote users and smartphones accessing email through IMAP and SMTP connections.
Mail server architecture on Debian with Postfix and Dovecot hosted on an OVHcloud VPS.

💡 Note: his tutorial focuses on configuring the mail server only. If you’d like to add a webmail interface, you can follow my separate guide on installing and configuring Roundcube.

Understanding Postfix and Dovecot: How They Work Together on a Mail Server

To make our setup work, we need to install two key applications on our Debian server: Postfix for SMTP/SMTPS and Dovecot for IMAP/IMAPS. In short, SMTP (Simple Mail Transfer Protocol) handles the sending and routing of emails between mail servers, while IMAP (Internet Message Access Protocol) allows clients to access and synchronize their mailboxes across multiple devices. In addition to managing incoming mail, Dovecot also handles user authentication and can automatically organize messages into folders. For instance, you can define a rule to move every email sent to john@std.rocks into the folder John.

Postfix and SMTP: Sending Emails from Your Linux Mail Server

When you send an email from your SMTP client — for example, Mozilla Thunderbird — it is first transmitted to the Postfix service on your server, which then forwards it to its final destination.

Diagram showing how a Debian mail server using Postfix sends emails via SMTP ports 25 and 587 from a Thunderbird client to an external service like Gmail.
Email delivery process through Postfix (SMTP) from a Thunderbird client to Gmail on a Debian server.

Dovecot and IMAP: Accessing and Synchronizing Mailboxes Securely

As mentioned earlier, the IMAP protocol is used to synchronize emails and folders between the Dovecot server and the mail client. It allows users to access their mailboxes from multiple devices while keeping all messages and folders — such as Inbox, Sent, or Drafts — perfectly synchronized in real time.

Diagram showing how a Dovecot IMAP server on Debian synchronizes emails and folders via ports 143 and 993 between a mail client such as Thunderbird and the server mail.std.rocks.
Mailbox synchronization through Dovecot (IMAP/IMAPS) between a Debian server and an email client.

Choosing and Setting Up Your VPS for a Debian Mail Server

Now that the concepts of SMTP and IMAP are clear, we can focus on where to host our mail server. You could run it at home, but be aware that many Internet Service Providers block port 25 (SMTP), their IP ranges may be blacklisted or have a poor reputation, and managing reverse DNS (rDNS) can be tricky. In short, self-hosting from home is possible, but it can be complex to set up and maintain.

A better option — and the one used in this tutorial — is to host your mail server on a Virtual Private Server (VPS). You can choose any provider you prefer and select a configuration suited to your needs. For example, with OVHcloud, a VPS with 4 vCores, 8 GB RAM, and a 75 GB SSD is more than sufficient for a small to medium-sized mail architecture.

Screenshot of OVHcloud VPS configuration showing Debian 13 installation with 4 vCores, 8 GB RAM, 75 GB SSD and 400 Mbps bandwidth, suitable for hosting a Postfix and Dovecot mail server.
Example of a Debian VPS configuration on OVHcloud, ideal for hosting a Postfix and Dovecot mail server.

Of course, if you don’t already have one, you’ll also need to purchase your own domain name. It will be used to configure your mail server’s DNS records and create professional email addresses such as user@yourdomain.com.

Screenshot showing the OVHcloud interface used to search for and register a domain name such as std.rocks for a mail server.
Searching for and registering a domain name on OVHcloud — an essential step before configuring your mail server.

At this stage, you won’t have to spend much more money — just a bit of your time. 😉

Configuring DNS Records for SPF, DKIM, DMARC, and rDNS

Nowadays, to prevent spam (although spammers still manage to send emails 😅), major email providers such as Gmail, Microsoft, and others perform several checks before accepting messages from external mail servers. These checks are designed to verify whether the sending server is legitimate. The main protection mechanisms involved are SPF, DKIM, and reverse DNS (rDNS). In this section, I’ll explain how each of these mechanisms works to help you configure them properly on your own mail server.

DNS Prerequisites and Initial Configuration

To begin, we need to add a few DNS entries so that other mail servers know which IP address to contact when sending emails to our domain. This involves creating an A record for the IPv4 address, an AAAA record for the IPv6 address (if available), and an MX record that points to our mail server — in this example, mail.std.rocks.

  • mail IN A 203.0.113.1: associates mail.std.rocks with the IPv4 address 203.0.113.1.
  • mail IN AAAA 2001:0db8::dead:bee5: associates mail.std.rocks with the IPv6 address 2001:0db8::dead:bee5.
  • IN MX 1 mail.std.rocks.: defines mail.std.rocks as the mail exchange (MX) server for the std.rocks domain.
mail IN A 203.0.113.1
mail IN AAAA 2001:0db8::dead:bee5
IN MX 1 mail.std.rocks.
Diagram showing how DNS records such as MX, A, and AAAA direct email traffic from external providers like Gmail to a Debian mail server using Postfix.
How DNS MX, A, and AAAA records allow other mail servers to locate and deliver emails to your Debian Postfix server.

From your DNS dashboard, the configuration should look like this:

Screenshot of the OVHcloud DNS dashboard showing MX, A, and AAAA records for the std.rocks domain, linking to the mail server mail.std.rocks.
Example of DNS records configuration on OVHcloud — MX, A, and AAAA entries pointing to the mail server mail.std.rocks.
  • On your server, set the hostname to mail:
root@debian:~# hostnamectl set-hostname mail
  • Edit the /etc/hosts file to associate the hostname with your domain:
127.0.0.1       localhost
::1     localhost       ip6-localhost   ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

127.0.1.1	mail.std.rocks	mail
  • Verify that the /etc/hostname file reflects the new hostname:
mail
  • Finally, reboot your server to apply the changes:
root@debian:~# hostnamectl set-hostname mail

Configuring the SPF Record to Authorize Your Mail Server

The SPF (Sender Policy Framework) record allows a domain administrator to publicly specify which mail servers are authorized to send emails on behalf of that domain. This helps receiving mail servers verify the legitimacy of incoming messages and reduce spam.

  • Add the following entries to your DNS configuration to authorize your mail server:
    • mx: (optional) includes all servers listed as MX for the std.rocks domain, in this case mail.std.rocks.
    • include:mail.std.rocks: (optional) explicitly authorizes the server mail.std.rocks.
    • ip4:203.0.113.1 and ip6:2001:0db8::dead:bee5: specify the IPv4 and IPv6 addresses allowed to send emails.
    • -all: indicates that all other IP addresses not listed in the record are unauthorized and should be treated as spam sources.
"v=spf1 mx include:mail.std.rocks ip4:203.0.113.1 ip6:2001:0db8::dead:bee5 -all"
Diagram showing how SPF verification allows a mail server to confirm authorized IP addresses for a domain using DNS records.
SPF verification process — DNS confirms which IP addresses are authorized to send emails for the std.rocks domain.

Once the SPF record has been created, your DNS dashboard should display an entry similar to the one below:

Screenshot of the OVHcloud DNS dashboard showing an SPF record for the domain std.rocks authorizing specific IP addresses and mail servers to send emails.
Example of an SPF record correctly configured on the OVHcloud DNS dashboard for the domain std.rocks.
  • To verify your SPF configuration (replace std.rocks with your own domain name), run the following command:
john@debian:~$ host -t txt std.rocks 
std.rocks descriptive text "v=spf1 mx include:mail.std.rocks ip4:203.0.113.1 ip6:2001:0db8::dead:bee5 -all"

Configuring DKIM to Sign and Authenticate Your Emails

Overview

DKIM (DomainKeys Identified Mail) may seem a bit complex at first, but the concept is simple. It allows the sending mail server to digitally sign each outgoing message by adding a DKIM-Signature field to the email header, using a private key (we’ll see how to create it later). The receiving server can then verify the authenticity of the message using the corresponding public key, which is published in the domain’s DNS records. This public/private key pair ensures that the email truly originates from the authorized domain and has not been altered during transmission.

Diagram showing how DKIM verification works: the sending server signs emails with a private key, and the receiving server verifies the signature using the public key stored in DNS.
DKIM verification process — the mail server signs outgoing messages with a private key, while the receiving server uses the public DNS key to validate their authenticity.

Configuration

  • On your server, install OpenDKIM and its tools:
root@debian:~# apt update && apt install opendkim opendkim-tools

💡 Note: You can customize your selector name (instead of simply using selector). Some administrators use the current year for clarity, for example: -s 2025.

  • Generate your key pair (private and public keys):
root@debian:~# sudo -u opendkim opendkim-genkey -D /etc/dkimkeys -d std.rocks -s selector
  • Create the directory /var/spool/postfix/opendkim/ with the correct permissions for the opendkim user:
root@debian:~# install -d -m 770 -o opendkim -g opendkim /var/spool/postfix/opendkim/
  • Add the postfix user to the opendkim group:
root@debian:~# usermod -aG opendkim postfix
  • Edit the /etc/opendkim.conf configuration file to define your domain, selector, and key paths:
Syslog                  yes
SyslogSuccess           yes
Canonicalization        relaxed/simple
OversignHeaders         From
Domain                  std.rocks
Selector                selector
KeyFile                 /etc/dkimkeys/selector.private
Socket                  local:/var/spool/postfix/opendkim/opendkim.sock
UserID                  opendkim
UMask                   007
PidFile                 /run/opendkim/opendkim.pid
TrustAnchorFile         /usr/share/dns/root.key
  • Restart the OpenDKIM service to apply the configuration changes:
root@debian:~# systemctl restart opendkim
  • Display the contents of the /etc/dkimkeys/selector.txt file to view your public key. This key will be required to create your DKIM DNS record:
root@debian:~# cat /etc/dkimkeys/selector.txt
selector._domainkey	IN	TXT	( "v=DKIM1; h=sha256; k=rsa; "
	  "p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlBBJYPEMqBqhBhap27waAwkW4ldwhhzxZErIeOagJSstm1gzExVvVuvRStv+hBk+IIwsNCr5OulWf7nZDThKWzUQs4fo2IBOcijbMcET48hgpnRiApKQvyRnxssuAjl9180u13pA3M5D35FYhGT0tNzEZLgI7YMN/nV00rvGT/RlzP7oa/XjcP73Zk+8R9YQhBviqPpHBjPyPK"
	  "369MP0zgYiVppjVedFive6kR3xciZ1BGbmSyM7tMFqZA3xVgcLtKQNMULdlzO+xz5h7e0u8zQBTPejjuC8fNiEhndbm9kmKIXV3G0PgsFKMd8S0ooH/L6ROM6+3MCrygKBLOcmxwIDAQAB" )  ; ----- DKIM key selector for std.rocks
  • Add the following entry to your DNS configuration. Each parameter defines a specific element of the DKIM record:
  • v=DKIM1: specifies the DKIM version used.
  • h=sha256: indicates the hashing algorithm (SHA-256) used for signing.
  • k=rsa: specifies the type of encryption key (RSA).
  • p=: contains your public key generated earlier.
selector._domainkey IN TXT "v=DKIM1; h=sha256; k=rsa; " "p=PUBLIC_KEY"
  • Depending on your DNS provider, the DKIM record setup may vary slightly. Here is an example from OVHcloud showing how to add the public key to the DNS zone:
Screenshot of the OVHcloud DNS configuration form showing how to add a DKIM record with the subdomain selector._domainkey, version DKIM1, hash algorithm sha256, and public key field.
Example of DKIM DNS record creation on OVHcloud — specifying the selector._domainkey subdomain, v=DKIM1 version, h=sha256 hash algorithm, and public key.
  • Once the record is saved, you should see the new DKIM entry displayed in your DNS dashboard, as shown below:
Screenshot of the OVHcloud DNS dashboard showing a DKIM record with the selector._domainkey subdomain, DKIM version, RSA key type, and base64-encoded public key for std.rocks.
Example of a DKIM record successfully added in the OVHcloud DNS dashboard for the domain std.rocks.
  • You can verify your DKIM configuration by querying your DNS record (replace std.rocks with your own domain):
john@debian:~$ host -t host -t TXT selector._domainkey.std.rocks
selector._domainkey.std.rocks descriptive text "v=DKIM1;h=sha256;k=rsa;p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlBBJYPEMqBqhBhap27waAwkW4ldwhhzxZErIeOagJSstm1gzExVvVuvRStv+hBk+IIwsNCr5OulWf7nZDThKWzUQs4fo2IBOcijbMcET48hgpnRiApKQvyRnxssuAjl9180u13pA3M5D35FYhGT0tNzEZLgI7YMN/nV00rvGT/RlzP7oa/Xjc" "P73Zk+8R9YQhBviqPpHBjPyPK369MP0zgYiVppjVedFive6kR3xciZ1BGbmSyM7tMFqZA3xVgcLtKQNMULdlzO+xz5h7e0u8zQBTPejjuC8fNiEhndbm9kmKIXV3G0PgsFKMd8S0ooH/L6ROM6+3MCrygKBLOcmxwIDAQAB;t=s;"

Implementing DMARC to Protect Your Domain from Email Spoofing

Overview

DMARC (Domain-based Message Authentication, Reporting, and Conformance) defines a policy that tells receiving mail servers how to handle messages claiming to come from your domain if they fail SPF or DKIM checks. While these servers are not strictly required to follow your policy, most major providers respect it. DMARC also allows you to specify email addresses where you can receive reports about authentication failures and domain abuse attempts.

Diagram showing how DMARC policies define actions for emails failing SPF or DKIM validation, with reporting options for the domain std.rocks.
DMARC settings — defining how receiving servers should handle emails that fail SPF or DKIM checks for the domain std.rocks.

Configuration

  • From the OVHcloud dashboard, you can add your DMARC record. The interface has some limitations — for example, it may not allow full customization of certain parameters like DKIM alignment. In that case, you can switch to the text format editor to manually edit the _dmarc record and use the following configuration:
_dmarc        IN TXT     "v=DMARC1;p=quarantine;rua=mailto:dmarc-reports@std.rocks;ruf=mailto:dmarc-fails@std.rocks;sp=reject;aspf=s;adkim=s;"
Screenshot showing how to create a DMARC DNS entry on OVHcloud, including parameters for quarantine policy, report addresses, and alignment modes.
Example of DMARC record configuration on OVHcloud — defining the policy (p=quarantine), reporting addresses, and strict alignment for SPF and DKIM.
  • Then you should see this new entry:
Screenshot of the OVHcloud DNS dashboard showing a DMARC record for the domain std.rocks with a quarantine policy, report addresses, and strict alignment.
Example of a DMARC record successfully added to the OVHcloud DNS dashboard for the domain std.rocks.
  • You can verify your DMARC configuration by querying your DNS record (replace std.rocks with your own domain):
john@debian:~$ host -t TXT _dmarc.std.rocks
_dmarc.std.rocks descriptive text "v=DMARC1;p=quarantine;rua=mailto:dmarc-reports@std.rocks;ruf=mailto:dmarc-fails@std.rocks;sp=reject;aspf=s;adkim=s;"

Configuring Reverse DNS (rDNS) for Your Mail Server

Reverse DNS (rDNS) is a special type of DNS record that resolves an IP address back to a domain name. When a receiving mail server gets an incoming message, it may perform an rDNS lookup to verify that the sending IP address corresponds to the hostname of the mail server — mail.std.rocks in our example. This helps confirm the legitimacy of the server and prevents messages from being flagged as suspicious or spam.

Diagram showing how reverse DNS resolves an IP address to a mail server hostname, verifying that 203.0.113.1 matches mail.std.rocks.
Reverse DNS verification — checking that the sending server’s IP address matches its hostname (mail.std.rocks).

In most cases, rDNS (reverse DNS) configuration must be requested from your IP address provider. However, if you are using a VPS, you can usually set it directly from your hosting control panel. For example, on OVHcloud, you can configure the reverse DNS entry from the dashboard interface, as shown below:

Screenshot of the OVHcloud interface showing how to configure reverse DNS for a VPS IP address, linking it to the hostname mail.std.rocks.
Reverse DNS configuration on OVHcloud — linking your VPS IP address to the mail server hostname mail.std.rocks.
  • You can verify your rDNS configuration to ensure it is correctly set (replace the IP address with your own):
john@debian:~$ host -t PTR 203.0.113.1
1.113.0.203.in-addr.arpa domain name pointer mail.std.rocks.

Installing and Configuring Postfix on Debian

Now that our DNS settings are properly configured, we can proceed with the installation of our SMTP server, Postfix! To better understand how it works, Postfix listens on three different ports, each serving a specific purpose. The first one, 25/tcp, is used for communication between mail servers — for example, to receive messages from external servers or to deliver outgoing emails to other domains. The other two ports, 465/tcp (implicit TLS) and 587/tcp (STARTTLS), are reserved for authenticated clients, such as email applications or mobile devices, to securely send outgoing messages via our mail server.

Diagram showing Postfix on Debian handling SMTP connections on ports 25, 465, and 587 for both external mail servers and authenticated clients using TLS and STARTTLS.
Postfix SMTP port configuration — ports 25, 465, and 587 used for external servers and authenticated clients with TLS encryption.

Installing Postfix

  • Install the Postfix package:
root@debian:~# apt update && apt install postfix
  • During installation, when prompted to choose the configuration type, select No configuration. This will allow you to manually set up Postfix later according to your custom mail server setup.
Screenshot of the Postfix installation prompt on Debian showing the 'No configuration' option selected during setup.
Postfix installation prompt — select No configuration to perform a manual setup later.

Configuring the Postfix main.cf File

💡 Note: We’ll use the virtual mail feature (via the virtual_alias_maps parameter), which means that email addresses do not need to be linked to actual Linux user accounts in /etc/passwd.

  • Create the /etc/postfix/main.cf file and adjust the configuration using your own hostname and domain name:
# See /usr/share/postfix/main.cf.dist for a commented, more complete version


# Debian specific:  Specifying a file name will cause the first
# line of that file to be used as the name.  The Debian default
# is /etc/mailname.
myorigin = /etc/mailname
myhostname = mail.std.rocks

smtpd_banner = $myhostname ESMTP $mail_name (Debian/GNU)
biff = no

# appending .domain is the MUA's job.
append_dot_mydomain = no

# See http://www.postfix.org/COMPATIBILITY_README.html -- default to 3.6 on
# fresh installs.
compatibility_level = 3.6

# ---- TLS (server side, SMTP port 25) ----
# Opportunistic TLS on SMTP (port 25). This is the right default for MX traffic.
smtpd_tls_security_level=may

# Self-signed certs for now; swap to Let's Encrypt later.
smtpd_tls_cert_file= /etc/ssl/certs/mail-selfsigned.crt
smtpd_tls_key_file= /etc/ssl/private/mail-selfsigned.key

# Disable legacy protocols; allow TLSv1.2/1.3.
smtpd_tls_protocols = !SSLv2,!SSLv3,!TLSv1,!TLSv1.1
smtpd_tls_mandatory_protocols = !SSLv2,!SSLv3,!TLSv1,!TLSv1.1

# Small, safe TLS hardening and visibility.
smtpd_tls_loglevel = 1
smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache

# ---- TLS (client side, outbound) -----
smtp_tls_security_level=may
smtp_tls_CApath=/etc/ssl/certs
smtp_tls_loglevel = 1
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache

# ---- Relay policy ----
# Allow SASL-authenticated users to relay; reject others (prevents open relay).
smtpd_relay_restrictions = permit_sasl_authenticated, reject_unauth_destination

# ---- Local delivery & networking -----
mydestination = $myhostname, localhost.localdomain, localhost
relayhost = 
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128
mailbox_size_limit = 0
#recipient_delimiter = +
inet_interfaces = all
inet_protocols = all

# ---- Virtual domains & Dovecot LMTP ----
alias_maps = 
alias_database = 
virtual_mailbox_domains = std.rocks
virtual_alias_maps = hash:/etc/postfix/virtual
virtual_mailbox_base = /mails/
# IMPORTANT: verify recipients; use a map (hash/sql/ldap) or you'll accept unknown users.
# might be worth activating in the future:
# virtual_mailbox_maps = hash:/etc/postfix/virtual_mailbox
virtual_uid_maps = static:7200
virtual_gid_maps = static:7200
virtual_minimum_uid = 7200

# Hand off mail to Dovecot via LMTP (fast and robust).
virtual_transport = lmtp:unix:private/dovecot-lmtp

# ---- Authentication (SASL via Dovecot) ----
smtpd_sasl_auth_enable = yes
smtpd_sasl_security_options = noanonymous
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
# Only allow AUTH over TLS (good hygiene).
smtpd_tls_auth_only = yes

# ---- SMTP hygiene (early rejections reduce spam and load) ----
# Require a proper HELO and do basic sender/recipient sanity checks.
smtpd_helo_required = yes
smtpd_recipient_restrictions = 
    reject_unauth_pipelining,
    reject_non_fqdn_sender,
    reject_non_fqdn_recipient,
    reject_unknown_sender_domain,
    reject_unknown_recipient_domain,
    permit_mynetworks,
    permit_sasl_authenticated,
    reject_unauth_destination

# Hide user enumeration via SMTP VRFY.
disable_vrfy_command = yes

# ---- DKIM via milter ----
milter_default_action = accept
smtpd_milters = unix:opendkim/opendkim.sock
non_smtpd_milters = $smtpd_milters

# ---- Misc ----
# ~25 MB per message (reasonable default).
message_size_limit = 25480000

Configuring the Postfix master.cf File

The Postscreen daemon acts as the first security layer for incoming mail connections. It listens on port 25/tcp and performs several preliminary checks, such as verifying the sender’s IP reputation or detecting blacklisted addresses. If all checks pass, Postscreen forwards the connection to the smtpd daemon for standard SMTP processing.

Diagram showing Postscreen acting as a security layer on port 25, filtering SMTP connections from external mail servers before passing them to the Postfix smtpd daemon on Debian.
Postscreen filtering process — checks the IP reputation of incoming SMTP connections before passing them to smtpd.
  • Edit the /etc/postfix/master.cf file to configure the active Postfix services:
# ========== Public SMTP (port 25) ==========
smtp      inet  n       -       y       -       1       postscreen
smtpd      pass  -       -       y       -       -       smtpd
#smtpd     pass  -       -       y       -       -       smtpd
dnsblog   unix  -       -       y       -       0       dnsblog
tlsproxy  unix  -       -       y       -       0       tlsproxy
# Choose one: enable submission for loopback clients only, or for any client.
#127.0.0.1:submission inet n -   y       -       -       smtpd

# ========== Submission (port 587) ==========
submission inet n       -       y       -       -       smtpd
  -o syslog_name=postfix/submission
  -o smtpd_tls_security_level=encrypt
  -o smtpd_tls_protocols=!SSLv2,!SSLv3,!TLSv1,!TLSv1.1
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_helo_required=yes
  -o milter_macro_daemon_name=ORIGINATING
  -o smtpd_relay_restrictions=permit_sasl_authenticated,reject
  -o smtpd_client_restrictions=
# ========== SMTPS (port 465) ==========
smtps     inet  n       -       y       -       -       smtpd
  -o syslog_name=postfix/smtps
  -o smtpd_tls_wrappermode=yes
  -o smtpd_tls_protocols=!SSLv2,!SSLv3,!TLSv1,!TLSv1.1
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_helo_required=yes
  -o milter_macro_daemon_name=ORIGINATING
  -o smtpd_relay_restrictions=permit_sasl_authenticated,reject
  -o smtpd_client_restrictions=
  • Create the vmail virtual user that will own all mail directories and files:
root@debian:~# groupadd -g 7200 vmail
root@debian:~# useradd -r -u 7200 -g 7200 -d /mails -m -s /usr/sbin/nologin vmail
  • Add your domain name to the /etc/mailname file:
root@debian:~# echo "std.rocks" > /etc/mailname
  • Generate a self-signed SSL/TLS certificate for securing email transmission:
root@debian:~# openssl req -new -x509 -days 365 -nodes \
  -out /etc/ssl/certs/mail-selfsigned.crt \
  -keyout /etc/ssl/private/mail-selfsigned.key \
  -subj "/C=US/ST=Washington/L=Seattle/O=std.rocks/OU=Mail/CN=mail.std.rocks"
root@debian:~# chmod 600 /etc/ssl/private/mail-selfsigned.key
root@debian:~# chown root:root /etc/ssl/private/mail-selfsigned.key
  • Create the main mail directory /mails/std.rocks to store all virtual mailboxes and set the proper permissions:
root@debian:~# mkdir -p /mails/std.rocks
root@debian:~# chown -R vmail:vmail /mails/std.rocks

Installing and Configuring Dovecot on Debian

It’s now time to install and configure Dovecot, which will handle both IMAP services and user authentication for our mail server.

Installing Dovecot

  • Install the required Dovecot packages:
root@debian:~# apt update && apt install dovecot-imapd dovecot-lmtpd dovecot-core dovecot-sieve

Configuring Dovecot

  • When you install Dovecot, it creates multiple configuration files by default. To make the setup cleaner and easier to understand, you can remove the default files and create a single configuration file that consolidates all the necessary settings.
root@debian:~# ls /etc/dovecot/conf.d/
10-auth.conf	 10-master.conf   15-lda.conf	     20-lmtp.conf	  90-fts.conf		     90-sieve.conf	   auth-oauth2.conf.ext      auth-static.conf.ext
10-logging.conf  10-metrics.conf  15-mailboxes.conf  30-dict-server.conf  90-quota.conf		     auth-deny.conf.ext    auth-passwdfile.conf.ext  auth-system.conf.ext
10-mail.conf	 10-ssl.conf	  20-imap.conf	     90-acl.conf	  90-sieve-extprograms.conf  auth-master.conf.ext  auth-sql.conf.ext
root@debian:~# rm /etc/dovecot/conf.d/*
  • Create the main configuration file at /etc/dovecot/conf.d/dovecot.conf:
#/etc/dovecot/conf.d/10-ssl.conf
ssl = yes
# Preferred permissions: root:root 0444
ssl_server_cert_file = /etc/dovecot/private/dovecot.pem
# Preferred permissions: root:root 0400
ssl_server_key_file = /etc/dovecot/private/dovecot.key

ssl_server_dh_file = /usr/share/dovecot/dh.pem

ssl_min_protocol = TLSv1.2
ssl_server_prefer_ciphers = client

#/etc/dovecot/conf.d/10-master.conf
protocols = lmtp imap

service lmtp {
        unix_listener /var/spool/postfix/private/dovecot-lmtp {
                mode = 0600
                user  = postfix
                group = postfix
        }
}

listen = 0.0.0.0
service imap-login {
        inet_listener imaps {
                port = 993
        }
        inet_listener imaps-v6 {
                port = 64993
                ssl = yes
        }
}
#Authentification postfix :
service auth {
  unix_listener /var/spool/postfix/private/auth {
    mode = 0666
    user = postfix
    group = postfix
  }
}

#/etc/dovecot/conf.d/auth-passwdfile.conf.ext
passdb passwd-file {
        driver = passwd-file
        passwd_file_path = /etc/dovecot/passwd
        auth_username_format = %{user | username}
}

userdb passwd-file {
        driver = passwd-file
        auth_username_format = %{user | username}
        passwd_file_path = /etc/dovecot/passwd
}

#sieve
protocol lmtp {
  mail_plugins = sieve
}
sieve_script personal {
  path = ~/.dovecot.sieve
}

#/etc/dovecot/conf.d/10-mail.conf
mail_driver = maildir
mail_home = /mails/%{user | domain}/%{user | username}
mail_path = %{home}/Maildir
mail_uid = vmail
mail_gid = vmail

# To further reduce iops on the metacache volume when using zlib or mail_crypt; point the dovecot temp directory to a tmpfs volume. Source : https://doc.dovecot.org/configuration_manual/os/
mail_temp_dir = /dev/shm/
  • Create the /etc/dovecot/passwd file, which will store user authentication credentials:
root@debian:~# touch /etc/dovecot/passwd
  • Set the correct permissions to secure the file:
root@debian:~# chown root:dovecot /etc/dovecot/passwd
root@debian:~# chmod 640 /etc/dovecot/passwd

Creating a Mail User

First, we need to generate a password hash for our mail user. We’ll use the doveadm command, which provides a built-in password hashing utility. The generated hash will then be added to the /etc/dovecot/passwd file to define the user’s credentials.

💡 Note: The ARGON2ID hashing algorithm is more secure than SHA512-CRYPT. However, if your system does not support Argon2, you can safely use doveadm pw -s SHA512-CRYPT instead — it still provides strong protection.

  • Use the doveadm pw command to generate the hash of your password (don’t use my weak example 😉), then copy the resulting hash:
root@debian:~# doveadm pw -s ARGON2ID
Enter new password:youWon'tfindMyPassWD
Retype new password:youWon'tfindMyPassWD
{ARGON2ID}$argon2id$v=19$m=65536,t=3,p=1$4IPe7Nj3kTZJ54f5sKnc0Q$1rIsKMMkz5wOeuIWZmHmk+aUBBvtiBAZTFtckKKsnfg
  • Edit the /etc/dovecot/passwd file and add the following line to create the john@std.rocks user:
john:{ARGON2ID}$argon2id$v=19$m=65536,t=3,p=1$4IPe7Nj3kTZJ54f5sKnc0Q$1rIsKMMkz5wOeuIWZmHmk+aUBBvtiBAZTFtckKKsnfg::::::
  • You may notice the :::::: after the hashed password. These fields are optional, but here’s what each one represents and how you can use them:
  • username: the user’s login name — in this case, john.
  • {ARGON2ID}$argon2id: the password hash generated with doveadm pw.
  • UID:GID: (optional) Unix user and group IDs. Since we use virtual mail, we can define 7200:7200.
  • home: (optional) the path to the user’s mailbox, for example: /mails/std.rocks/john.
  • shell: (optional) defines the user’s shell, if needed.
  • extra_fields: (optional) can include additional parameters such as quotas, e.g. userdb_mail=maildir:/mails/%d/%n, userdb_quota_rule=*:storage=5G.
  • Create the /etc/postfix/virtual file to define email aliases:
root: john@std.rocks
letsencrypt@std.rocks john@std.rocks
root@debian:~# postmap /etc/postfix/virtual

Before creating the IMAP folders, it’s helpful to visualize how the mail client — such as Thunderbird — corresponds to the folder structure on our mail server.

Diagram showing how IMAP folders on a Dovecot mail server, such as Inbox, Sent, Drafts, Junk, and Trash, map to corresponding folders in the Thunderbird email client for the user john@std.rocks.
IMAP folder mapping — how folders stored under /mails/std.rocks/john correspond to mailbox folders in an email client like Thunderbird.
  • Once the folder structure has been visualized, create the main directory for the user john:
root@debian:~# install -d -m 700 -o vmail -g vmail /mails/std.rocks/john
  • Use the maildirmake.dovecot command to create the Maildir structure and its subfolders (Drafts, Sent, Trash, and Junk):
root@debian:~# sudo -u vmail maildirmake.dovecot /mails/std.rocks/john/Maildir
root@debian:~# sudo -u vmail maildirmake.dovecot /mails/std.rocks/john/Maildir/.Drafts
root@debian:~# sudo -u vmail maildirmake.dovecot /mails/std.rocks/john/Maildir/.Sent
root@debian:~# sudo -u vmail maildirmake.dovecot /mails/std.rocks/john/Maildir/.Trash
root@debian:~# sudo -u vmail maildirmake.dovecot /mails/std.rocks/john/Maildir/.Junk
  • Restart both services to apply the configuration:
root@debian:~# systemctl restart postfix.service
root@debian:~# systemctl restart dovecot.service

Securing Your Mail Server with a Let’s Encrypt SSL Certificate

At this point, our mail server should be fully operational. We’ll now install Let’s Encrypt SSL certificates to secure our SMTPS and IMAPS services. This approach is more convenient and prevents warning messages when connecting from email clients.

Generating Let’s Encrypt SSL Certificates

  • Install the certbot package:
root@debian:~# apt update && sudo apt install certbot
  • Use certbot to request your SSL certificates. Replace mail.std.rocks with your own mail domain and letsencrypt@std.rocks with your administrative email address:
root@debian:~# certbot certonly --standalone -d mail.std.rocks --email letsencrypt@std.rocks --agree-tos --noninteractive
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Requesting a certificate for mail.std.rocks

Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/mail.std.rocks/fullchain.pem
Key is saved at:         /etc/letsencrypt/live/mail.std.rocks/privkey.pem
This certificate expires on 2026-02-09.
These files will be updated when the certificate renews.
Certbot has set up a scheduled task to automatically renew this certificate in the background.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
If you like Certbot, please consider supporting our work by:
 * Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
 * Donating to EFF:                    https://eff.org/donate-le
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

💡 Note: Keep track of your certificate’s expiration date. Certbot automatically sets up renewal, but you can also configure a cron job or systemd timer to ensure the process runs smoothly.

Integrating Let’s Encrypt Certificates with Dovecot and Postfix

  • Edit your /etc/postfix/main.cf file and update the paths to use your Let’s Encrypt certificates instead of the self-signed ones:
# Self-signed certificates (previous configuration)
#smtpd_tls_cert_file = /etc/ssl/certs/mail-selfsigned.crt
#smtpd_tls_key_file = /etc/ssl/private/mail-selfsigned.key

# Let’s Encrypt certificates (active configuration)
smtpd_tls_cert_file = /etc/letsencrypt/live/mail.std.rocks/fullchain.pem
smtpd_tls_key_file = /etc/letsencrypt/live/mail.std.rocks/privkey.pem
  • Restart the Postfix service to apply the new SSL configuration:
root@debian:~# systemctl restart postfix.service
  • Edit your /etc/dovecot/conf.d/dovecot.conf file to point to the Let’s Encrypt certificate and key files:
# Preferred permissions: root:root 0444
#ssl_server_cert_file = /etc/ssl/certs/fullchain.pem
ssl_server_cert_file = /etc/letsencrypt/live/mail.std.rocks/fullchain.pem

# Preferred permissions: root:root 0400
#ssl_server_key_file = /etc/ssl/private/privkey.pem
ssl_server_key_file = /etc/letsencrypt/live/mail.std.rocks/privkey.pem
  • Restart the Dovecot service to load the updated certificates:
root@debian:~# systemctl restart dovecot.service

Email Client Integration

Our mail server is now fully operational, so we can configure our email clients — such as Thunderbird on desktop and K-9 Mail on mobile devices — to connect securely using IMAP and SMTP.

Thunderbird

In the account setup window, click on Configure manually to enter the server settings manually.

Screenshot of the Thunderbird email setup window showing fields for full name, email address john@std.rocks, password, and the Configure manually button.
Thunderbird account setup — fill in your full name, email address, and password, then click Configure manually to define the server settings.

Configure the account with your username, password, and server name. For IMAP, you can also use STARTTLS on port 143 if preferred.

Screenshot of Thunderbird manual configuration showing IMAP on port 993 with SSL/TLS and SMTP on port 587 with STARTTLS for the account john@std.rocks.
Thunderbird manual configuration — IMAP set to port 993 with SSL/TLS and SMTP set to port 587 with STARTTLS, both using john@std.rocks as the username.

K-9 Mail

On Android, you can use the K-9 Mail application to connect to your mail server. As with Thunderbird, replace the example values with your own credentials and server settings. In the final configuration step, manually map the special folders — Sent, Drafts, Junk, and Trash — to ensure proper synchronization.

Screenshots of the K-9 Mail Android app showing IMAP configuration with mail.std.rocks on port 993 using SSL/TLS, SMTP on port 587 using STARTTLS, and special folder mapping for Sent, Drafts, Junk, and Trash.
K-9 Mail configuration on Android — IMAP set to port 993 with SSL/TLS, SMTP set to port 587 with STARTTLS, and manual mapping of special folders for optimal synchronization.

Congratulations! 🎉 You now have a fully functional mail server. You can continue your journey toward digital independence by installing a shared calendar with Baïkal on the same server.