Skip to main content

    How to Install an SSL Certificate on Postfix and Dovecot

    Install a TLS certificate on Postfix and Dovecot: the chain file Postfix wants, the settings Dovecot 2.4 renamed, and how to check it is really live.

    MS
    My-SSL Team
    ·
    16 min read
    ·
    Published August 7, 2026
    ·
    Last updated August 7, 2026

    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.

    How the same issued certificate is packaged differently for Postfix and for DovecotOne issuance from the certificate authority produces three objects: a private key, a server certificate, and one or more intermediate certificates. The left panel shows Postfix, which since version 3.4 reads a single file through the smtpd_tls_chain_files setting, with the objects stacked in a fixed order highlighted in gold: private key first, then the server certificate, then the intermediates. The right panel shows Dovecot, which reads two separate files: ssl_server_key_file holds the private key alone, and ssl_server_cert_file holds the server certificate followed by the intermediates. The same issued certificate feeds both layouts; only the packaging differs.One issued certificate, two ways of packaging itWhat the CA gives youprivate key · server certificate · intermediatesPOSTFIX 3.4 AND LATER — ONE FILE/etc/ssl/mail/chain.pem1 · PRIVATE KEYBEGIN PRIVATE KEY2 · SERVER CERTIFICATEBEGIN CERTIFICATE3 · INTERMEDIATE(S)BEGIN CERTIFICATEsmtpd_tls_chain_filesorder is not optionalDOVECOT — TWO FILESprivkey.pemPRIVATE KEY, ON ITS OWNssl_server_key_filefullchain.pemCERTIFICATE + INTERMEDIATESno key in this filessl_server_cert_filesetting names shown are Dovecot 2.42.3 calls them ssl_key and ssl_certNothing about the certificate changes between the two panels. Only how the bytes are arranged on disk.
    Copying the Postfix chain file straight into Dovecot's certificate setting is the most common way this install goes wrong: Dovecot then hands every connecting client a file that opens with your private key.

    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.6

    On 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/certs

    On 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 = may

    Worth 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.

    Which TLS mode belongs on each mail port, and which daemon serves itFour rows map mail ports to their TLS mode and owning daemon. Port 25 carries inbound mail from other servers and is highlighted in gold as the exception: it uses opportunistic STARTTLS with the Postfix setting smtpd_tls_security_level set to may, because a public MX must still accept senders that offer no encryption. Port 465 is submission over implicit TLS, served by Postfix with wrappermode enabled. Port 587 is submission over STARTTLS, where Postfix should require encryption. Ports 993 and 995 are IMAP and POP3 over implicit TLS, served by Dovecot. The same certificate answers on all four.One certificate, four listeners, and one port that must stay lenientPORTWHO CONNECTSTLS MODEDAEMON25inbound MXOther mail serversstrangers, unauthenticatedOpportunistic STARTTLSsecurity_level = maynever encrypt — you would lose mailPostfix465submissionsYour own usersauthenticated sendersImplicit TLStls_wrappermode = yesPostfix587submissionYour own usersolder client configsRequired STARTTLSsecurity_level = encryptPostfix993 / 995IMAPS / POP3SYour own usersreading mailImplicit TLSssl = yesDovecotPort 25 is the only row where refusing an unencrypted connection costs you real mail.Everywhere else, the client is someone you already authenticate.
    The gold row is the one people get wrong after reading a hardening guide written for submission ports, and the resulting mail loss is silent at your end.

    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,reject

    RFC 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 = yes

    For 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 = yes

    Note 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.

    The Dovecot SSL settings that were renamed between version 2.3 and version 2.4A two-column comparison of Dovecot SSL configuration syntax. The left column shows Dovecot 2.3, where certificate settings are written as ssl_cert, ssl_key and ssl_dh, each with a leading angle bracket that instructs Dovecot to read the file's contents. The right column, highlighted in gold, shows Dovecot 2.4, where the same settings are named ssl_server_cert_file, ssl_server_key_file and ssl_server_dh_file, and the angle bracket is removed because the settings take a path directly. Feeding a 2.3 configuration to Dovecot 2.4 produces an unknown setting error at start-up, and the reverse fails the same way.The same three settings, renamed in Dovecot 2.4DOVECOT 2.3ssl_cert = </etc/ssl/fullchain.pemssl_key = </etc/ssl/privkey.pemssl_dh = </etc/dovecot/dh.pemThe leading < means"read this file's contents".Rejected by 2.4.DOVECOT 2.4ssl_server_cert_file =/etc/ssl/fullchain.pemssl_server_key_file =/etc/ssl/privkey.pemssl_server_dh_file =/etc/dovecot/dh.pemNo angle bracket. Paths only.Mixing them up looks like this at start-up:doveconf: Fatal: ... Unknown setting: ssl_certCheck which branch you are on with dovecot --version before you edit anything.Almost every mail tutorial still online was written for the left-hand column.
    This rename is why a mail server that ran for years can fail to start after a routine distribution upgrade, with a certificate that was never the problem.

    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 dovecot

    Running 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.com

    Three 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.

    Why a renewed mail certificate still serves the expired one without a daemon reloadTwo timelines compare renewal outcomes. The upper timeline, marked as broken, shows a certificate installed, then renewed on disk after 200 days, while Postfix and Dovecot continue serving the old certificate they loaded at start-up, ending in client errors. The lower timeline, highlighted in gold, adds a reload step immediately after renewal, so the new certificate is served and clients connect normally. A note records that the CA/Browser Forum maximum certificate lifetime dropped to 200 days on March 15, 2026 and falls to 100 days on March 15, 2027, so this cycle repeats more often each year.The renewal is the easy half. The reload is the half that gets forgotten.WITHOUT A RELOAD HOOKinstallfiles renewed on diskcron reports successclients failexpired cert still in memoryup to 200 daysWITH A RELOAD HOOKinstallfiles renewedpostfix reloadsystemctl reload dovecotservedMaximum public certificate lifetime fell to 200 days on March 15, 2026, and drops to 100 days on March 15, 2027.Whatever you skip today, you will skip roughly four times a year by 2027.
    A renewal job that never reloads the daemons looks healthy in every log it writes, which is why this failure usually surfaces as a user complaint rather than an alert.

    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 dovecot

    Make 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 seeUsual causeFix
    Unknown setting: ssl_certDovecot 2.3 syntax on a 2.4 installRename to ssl_server_cert_file and drop the angle bracket
    Postfix refuses to start after the editChain file out of order, or key and certificate do not matchRebuild key first, then certificate, then intermediates
    Works on a laptop, fails on a phoneIntermediate missing; the laptop had it cachedServe the full chain, not the leaf certificate alone
    Certificate renewed, clients still see the old oneNo reload after renewalAdd a deploy hook that reloads both daemons
    Hostname mismatch warning in the clientCertificate names the website, not the mail hostReissue with the mail hostname as a SAN
    Inbound mail volume drops after hardeningencrypt set on port 25Return 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

    Frequently Asked Questions

    Get instant answers to common questions about SSL certificates and our services.

    Still Have Questions?

    Our SSL experts are available 24/7 to help with any questions about certificates, installation, or technical issues.

    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