The short answer
Postfix and Dovecot share one certificate but want it packaged differently. On Postfix 3.4 and later, point smtpd_tls_chain_files at a single file holding the private key, then the certificate, then the intermediates, in that order. On Dovecot, keep them separate: the key in one file, the certificate plus intermediates in another. If you are on Dovecot 2.4, those settings are now called ssl_server_cert_file and ssl_server_key_file, with no leading angle bracket. Reload both daemons afterwards, or they keep serving the certificate they read at start-up.
On this page
- What you need before you start
- Which files each daemon actually wants
- Installing the certificate in Postfix
- Which TLS setting belongs on which port
- Installing the certificate in Dovecot
- Why your Dovecot config broke after an upgrade
- Reloading so the new file is actually served
- Verifying the certificate from outside
- Keeping it installed as lifetimes shorten
- Failures and what they mean
- FAQ
What you need before you start
Three files and one fact. The files are the private key you generated with the CSR, the issued server certificate, and the intermediate certificate your CA sends alongside it. The fact is which hostname the certificate covers, because Postfix and Dovecot will happily serve a certificate for the wrong name and let every client discover the problem for you.
Root or sudo access is assumed throughout, along with a Postfix and Dovecot pair that already delivers mail without TLS. If mail is not flowing yet, fix that first. Adding encryption to a broken transport turns one problem into two that look identical in the logs.
Check the name first. Run openssl x509 -in cert.pem -noout -subject -ext subjectAltName and confirm the hostname your mail clients are configured with appears in the output. If the certificate names your website but not your mail host, no amount of configuration will help. Which name belongs on a mail certificate covers how to work that out.
If you have not ordered the certificate yet, a mail host takes an ordinary publicly trusted server certificate rather than anything mail-specific. DV and OV SSL certificates from My-SSL cover any hostname you can validate, and you can add a second name for the MX target to the same request.
Which files each daemon actually wants
Postfix 3.4 and later read one combined file through smtpd_tls_chain_files, ordered private key first, then the server certificate, then any intermediates. Dovecot reads two files: the certificate and its intermediates in one, the private key alone in the other. Both are built from the same issuance. Neither layout works in the other daemon.
Somewhere to keep them matters more than it sounds. Put the pair under a directory only root can read, because the moment you start copying keys next to the certificate you invite a later mistake where the world-readable file is the one holding the key.
mkdir -p /etc/ssl/mail
chmod 700 /etc/ssl/mail
# Postfix: one file, key first
cat privkey.pem cert.pem intermediate.pem > /etc/ssl/mail/chain.pem
# Dovecot: two files, no key in the certificate file
cat cert.pem intermediate.pem > /etc/ssl/mail/fullchain.pem
cp privkey.pem /etc/ssl/mail/privkey.pem
chmod 600 /etc/ssl/mail/chain.pem /etc/ssl/mail/privkey.pem
chmod 644 /etc/ssl/mail/fullchain.pem
chown root:root /etc/ssl/mail/*If your CA delivered a PFX or P7B bundle rather than PEM files, convert before you go further; the guide to certificate formats has the openssl invocations for each direction. And if you are unsure which of the files your CA sent is the intermediate, how a certificate chain fits together explains what to look for in the issuer and subject lines.
Installing the certificate in Postfix
Set smtpd_tls_chain_files to your combined file and smtpd_tls_security_level to may in /etc/postfix/main.cf. That is the whole inbound configuration on a modern Postfix. Leave the older smtpd_tls_cert_file and smtpd_tls_key_file unset when you use the chain file, because Postfix treats the two interfaces as alternatives rather than layers.
Check your version before copying anything, since the chain-file interface arrived in Postfix 3.4 and most guides still online predate it:
postconf mail_version
# mail_version = 3.8.6On 3.4 or newer, this is the block to add:
# /etc/postfix/main.cf
smtpd_tls_chain_files = /etc/ssl/mail/chain.pem
smtpd_tls_security_level = may
smtpd_tls_loglevel = 1
# Outbound, when this server delivers to others
smtp_tls_security_level = may
smtp_tls_CApath = /etc/ssl/certsOn anything older than 3.4 you are back to the two-setting form, where the certificate file holds the server certificate followed by the intermediates and the key lives on its own:
# Postfix older than 3.4 only
smtpd_tls_cert_file = /etc/ssl/mail/fullchain.pem
smtpd_tls_key_file = /etc/ssl/mail/privkey.pem
smtpd_tls_security_level = mayWorth knowing: smtpd_tls_chain_files accepts more than one file, which is how a single host serves an RSA and an ECDSA certificate to whichever the client prefers. That is also why the key has to come first in each file: it is the marker that tells Postfix a new chain has started.
The setting named smtp_tls_security_level without the d governs mail this server sends outward, and it is easy to edit the wrong one at two in the morning. Inbound settings carry the smtpd prefix, for the daemon; outbound settings carry smtp, for the client.
Which TLS setting belongs on which port
Port 25 must stay at may, and the submission ports should require encryption. A public MX that refuses unencrypted connections loses legitimate mail from senders that never offer STARTTLS, and it loses it quietly. Ports 587 and 465 carry only your own authenticated users, so there is no interoperability cost to demanding TLS there.
Per-port settings live in /etc/postfix/master.cf, applied as -o overrides beneath each service line. Port 465 needs smtpd_tls_wrappermode=yes, because implicit TLS starts the handshake before any SMTP greeting rather than upgrading mid-conversation the way STARTTLS does:
# /etc/postfix/master.cf
submission inet n - y - - smtpd
-o syslog_name=postfix/submission
-o smtpd_tls_security_level=encrypt
-o smtpd_sasl_auth_enable=yes
-o smtpd_client_restrictions=permit_sasl_authenticated,reject
smtps inet n - y - - smtpd
-o syslog_name=postfix/smtps
-o smtpd_tls_wrappermode=yes
-o smtpd_sasl_auth_enable=yes
-o smtpd_client_restrictions=permit_sasl_authenticated,rejectRFC 8314 asks clients to prefer implicit TLS on 465 over STARTTLS on 587, and most mail clients now default that way when they autodiscover a server. Keep both listeners open regardless. Something in your estate is still configured for 587, and finding out which device it was after you closed the port is not a good afternoon.
Installing the certificate in Dovecot
Dovecot's SSL settings live in /etc/dovecot/conf.d/10-ssl.conf, and the setting names depend on which branch you run. Check with dovecot --version before you edit, because 2.3 and 2.4 each reject the other's syntax outright rather than warning about it.
For Dovecot 2.4, released in January 2025 and now the version arriving with current distributions:
# Dovecot 2.4 — /etc/dovecot/conf.d/10-ssl.conf
ssl = yes
ssl_server_cert_file = /etc/ssl/mail/fullchain.pem
ssl_server_key_file = /etc/ssl/mail/privkey.pem
ssl_min_protocol = TLSv1.2
ssl_prefer_server_ciphers = yesFor Dovecot 2.3, the settings carry the older names and each path is prefixed with an angle bracket, which is Dovecot's instruction to read the file rather than treat the value as literal text:
# Dovecot 2.3 — /etc/dovecot/conf.d/10-ssl.conf
ssl = yes
ssl_cert = </etc/ssl/mail/fullchain.pem
ssl_key = </etc/ssl/mail/privkey.pem
ssl_min_protocol = TLSv1.2
ssl_prefer_server_ciphers = yesNote which file goes where. The certificate setting takes the full chain, server certificate first and intermediates after it, because Dovecot sends exactly what that file contains and never fetches a missing issuer on its own. Hand it a file with only your server certificate and you get a chain error that appears on some clients and not others, depending on what each one already had cached.
Dovecot reads both files as root before dropping privileges, so the private key should stay at mode 600 and owned by root. There is no need to loosen it for the mail user, and doing so is a common piece of advice that solves a problem you do not have.
Why your Dovecot config broke after an upgrade
Dovecot 2.4 renamed the SSL settings and dropped the angle-bracket prefix, so a configuration that worked for years fails at start-up with an unknown-setting error. The mapping is direct: ssl_cert became ssl_server_cert_file, ssl_key became ssl_server_key_file, and ssl_dh became ssl_server_dh_file. The certificate itself is not involved.
This is worth flagging loudly because of what it does to search results. Dovecot 2.4 landed in January 2025, and essentially every mail-server tutorial written before then teaches the 2.3 syntax. Copy one of those onto a current system and the config fails immediately, which sends people looking for a certificate problem that does not exist.
Two smaller changes travel with it. The 2.4 default for ssl_min_protocol is already TLSv1.2, so you can drop the line unless you are deliberately raising it to TLSv1.3. And the broader 2.4 configuration overhaul touched more than TLS, so if the upgrade left other settings unrecognised, run doveconf -n and work through what it reports rather than fixing one line at a time.
Before you reload: doveconf -n parses the whole configuration and prints what Dovecot would actually use. It fails loudly on an unknown setting, which makes it the cheapest way to catch a rename before it takes IMAP down for everyone.
Reloading so the new file is actually served
Run postfix reload and systemctl reload dovecot. Both daemons hold the certificate they read at start-up, so editing the files on disk changes nothing that clients can see until one of those commands runs. Reload keeps established connections alive, which matters on IMAP where clients hold sessions open for hours.
postfix check && postfix reload
doveconf -n > /dev/null && systemctl reload dovecotRunning the validator first is the habit worth keeping. postfix check and doveconf -n both exit non-zero on a broken configuration, so chaining them with && means a typo stops the reload instead of taking the service down with it.
Verifying the certificate from outside
Connect with openssl s_client from a machine that is not the mail server and read the verify return code. A result of 0 (ok) means the certificate and its chain both check out against the local trust store. Anything else names the specific failure, which is far more useful than what a mail client will tell you.
# Inbound SMTP, STARTTLS on 25
openssl s_client -starttls smtp -connect mail.example.com:25 \
-servername mail.example.com
# Submission, STARTTLS on 587
openssl s_client -starttls smtp -connect mail.example.com:587 \
-servername mail.example.com
# Implicit TLS on 465 and IMAPS on 993 — no -starttls needed
openssl s_client -connect mail.example.com:465 -servername mail.example.com
openssl s_client -connect mail.example.com:993 -servername mail.example.comThree things in that output are worth reading rather than skimming. The certificate chain should list your server certificate and at least one intermediate; a chain one entry long means Dovecot or Postfix is serving the certificate without its issuer. The subject and SAN entries should contain the hostname you connected to. And the verify return code should be zero.
Add -showcerts when the chain looks wrong and you want to see every certificate the server actually sent. For a quick check on the certificate's dates and names without a connection, the My-SSL certificate tools will decode a PEM file or inspect a live host.
Test from off-network. A check run on the mail server itself often passes because the intermediate is already in that machine's trust store. The whole point of the exercise is to see what a stranger's client sees.
Keeping it installed as lifetimes shorten
Wire the reload into whatever renews the certificate, because the renewal alone does not reach the running daemons. As of March 15, 2026 a publicly trusted certificate may not exceed 200 days, and under the CA/Browser Forum schedule adopted in ballot SC-081v3 that ceiling falls to 100 days on March 15, 2027 and to 47 days on March 15, 2029. Every one of those cycles is another chance to forget the reload.
If you renew with Certbot, the deploy hook runs only when a certificate was actually replaced, which is what you want on a job that fires twice a day:
# /etc/letsencrypt/renewal-hooks/deploy/mail-reload.sh
#!/bin/sh
set -e
D=/etc/letsencrypt/live/mail.example.com
cat "$D/privkey.pem" "$D/cert.pem" "$D/chain.pem" > /etc/ssl/mail/chain.pem
cp "$D/fullchain.pem" /etc/ssl/mail/fullchain.pem
cp "$D/privkey.pem" /etc/ssl/mail/privkey.pem
chmod 600 /etc/ssl/mail/chain.pem /etc/ssl/mail/privkey.pem
postfix reload
systemctl reload dovecotMake it executable and test with certbot renew --dry-run. The same shape works for a commercial certificate; only the source of the files changes. The Certbot and ACME guide covers the renewal side in more depth, including why a mail host that answers no HTTP usually needs a DNS-01 challenge.
Monitor the expiry date from outside as well. A renewal job that breaks silently is the normal way mail certificates expire, and the first report almost always arrives as a user saying their phone stopped collecting mail.
Failures and what they mean
Most Postfix and Dovecot TLS failures come from one of four causes: the wrong objects in a file, the wrong order inside it, a missing intermediate, or a daemon that was never reloaded. The table below maps what you see to which of those it is.
| What you see | Usual cause | Fix |
|---|---|---|
Unknown setting: ssl_cert | Dovecot 2.3 syntax on a 2.4 install | Rename to ssl_server_cert_file and drop the angle bracket |
| Postfix refuses to start after the edit | Chain file out of order, or key and certificate do not match | Rebuild key first, then certificate, then intermediates |
| Works on a laptop, fails on a phone | Intermediate missing; the laptop had it cached | Serve the full chain, not the leaf certificate alone |
| Certificate renewed, clients still see the old one | No reload after renewal | Add a deploy hook that reloads both daemons |
| Hostname mismatch warning in the client | Certificate names the website, not the mail host | Reissue with the mail hostname as a SAN |
| Inbound mail volume drops after hardening | encrypt set on port 25 | Return port 25 to may |
For the first of those, the quickest confirmation is openssl rsa -in privkey.pem -noout -modulus | openssl md5 against the same command run on the certificate with openssl x509. Matching hashes mean the key belongs to the certificate, and you can stop suspecting the pair and start reading the file order.
Raise smtpd_tls_loglevel to 1 while you work, and drop it back afterwards. Level 1 records the handshake outcome per connection, which is enough to see whether clients are negotiating TLS at all. Higher levels write so much that finding the relevant line becomes its own problem.
Frequently asked questions
Need the certificate itself?
Everything above assumes an issued certificate covering your mail hostname. Browse DV and OV SSL certificates and put every mail name on one request. My-SSL is a Certum partner, so the certificates chain to roots that have been in the public trust stores for years, which is the property older mail clients on the far side of a connection depend on.
Related reading
- Which certificate a mail server needs — the step before this one, covering which hostnames belong in the request.
- PEM, DER, PFX and CRT explained — for turning whatever your CA sent into the files these daemons expect.
- Certbot and ACME in production — automating the renewal half, including DNS-01 for hosts that serve no HTTP.