Dovecot 2.3 to 2.4 update problems

Dovecot, which is a secure IMAP server, released version 2.4.0 on 24 January 2025 and a minor revision of 2.4.1 on 28 March 2025. As such, I recently decided to update my instances from the 2.3 branch (version 2.3.21.1 to be exact) to this new 2.4 branch. Though major revision updates can often be problematic, this one posed some additional challenges due to many backwards-incompatible changes. Though that linked upgrade documentation was helpful, I still ran into some problems with the update that I will explain in this post. Hopefully these tips will help others avoid some of the mistakes that I made.

My Dovecot IMAP configuration is rather simple, making some of the backwards-incompatible changes that applied to me easy enough to address:

  • Variable expansions (or so I thought; more on this one later)
  • Naming of userdb and passdb
    • These two directives now require a name, which can be the name of the lookup database or simply arbitrary
  • Arguments supplied to userdb and passdb separated into individual directives
    • Instead of having, say, passdb { args = username_format=%n $path_to_file }, the args are separated into:
      • passdb_driver = passwd-file
      • auth_username_format =
      • passwd_file_path = $path_to_file
  • Changes to the names of some directives (including SSL)
    • ssl_cert became ssl_server_cert_file
    • ssl_key became ssl_server_key_file
  • Separation of the mail_location directive into individual components
    • Instead of having mail_location = $mailbox_format:$mailbox_path (e.g. mail_location = maildir:/var/mail/), it is now:
      • mail_driver = $mailbox_format –> mail_driver = maildir
      • mail_path = $mailbox_path –> mail_path = /var/mail/
      • mail_home = $mailbox_home –> mail_home = /var/mail/home
  • Some new defaults for base settings
    • The base_dir now defaults to /var/run/dovecot, so it should be omitted and shouldn’t be changed
    • The listen directive now defaults to listen = *, ::, which will listen on all available IPv4 and IPv6 addresses
    • The disable_plaintext_auth directive was renamed to auth_allow_cleartext and now defaults to no for better security

I also took this upgrade window as an opportunity to shift all of my configuration from the included conf.d/ files to a single dovecot.conf file with all of my settings. Though I was able to make a copy of my Dovecot 2.3 configuration files that I could modify in preparation for the update to Dovecot 2.4, the new configuration didn’t work perfectly.

SSL errors when starting Dovecot

The first unexpected error I encountered was when trying to start Dovecot 2.4 with my new configuration. It refused to start, throwing an error message of ‘SSL certificates too long’. The problem here ended up being that certificates no longer need to be redirected into the directive. More simply put, the less-than sign (<) needed to be removed from the certificate lines:

In Dovecot 2.3:
ssl_cert = </etc/ssl/mail_server/fullchain.pem
ssl_key = </etc/ssl/mail_server/privkey.pem
In Dovecot 2.4
ssl_server_cert_file = /etc/ssl/mail_server/fullchain.pem
ssl_server_key_file = /etc/ssl/mail_server/privkey.pem
The important part here is that the ‘<‘ has been removed for each of the SSL server files (both cert and key).

Variable expansion not working as intended

With the documentation provided by the Dovecot team regarding changes to variables, I thought that the expansion would be a non-issue, but I was mistaken. After upgrading to Dovecot 2.4, I could no longer log in to any of my email addresses across multiple domains using any of my IMAP clients. Looking at the mail authentication logs, I found many error messages similar to this one:

Jul 03 00:57:06 [dovecot] auth(z@z-issue.com,$IP,sasl:login): passwd-file: missing passwd file: /var/mail/vhosts/%domain/shadow

This first error was a simple syntax mistake on my part where I accidentally omitted the needed curly braces around any variables. In particular, I referenced %domain instead of %{domain} in many directives, such as mail_home, mail_path, and for this exact error, the passwd_file_path line of passdb.

After correcting my mistake by adding the curly braces, I anticipated the problem would be resolved. However, I was still unable to log in using my IMAP clients. The error messages in the mail authentication logs did change, though, to ones like this:

Jul 03 01:14:52 [dovecot] auth(z@z-issue.com,$IP,sasl:cram-md5)<+ssPdf84tMAYz+f6>: Error: passwd-file: Failed to expand passwd-file path /var/mail/vhosts/%{domain}/shadow: domain: No value to get domain from

This error was more obtuse to me. The %{domain} variable should have been available based on the email address logging in. In this error message above, the email address (which is a fake example) of z@z-issue.com should have resulted in:

%{user} --> expanding to 'z'
%{domain} --> expanding to 'z-issue.com'

but it didn’t. Looking again at the Dovecot 2.4 upgrade documentation for variable expansion, I finally figured out what I needed to do. In the table under the ‘List of common short variables and their replacements‘, the row showing %d being replaced by %{user|domain} was confusing to me. I wrongly interpreted that syntax as the boolean “or” in Regular Expressions (RegEx), meaning that I can use %{user} as a variable containing the username portion of the email address, and %{domain} as a variable containing the domain portion of the email address.

My interpretation of that syntax was incorrect. The chart is showing the literal variable to use, and the pipe (or vertical bar) here is serving as a filter. That results in the variable expansion being:

Old variableNew syntaxExplanation
%d%{user|domain}Referencing the ‘user’ variable, extract only the ‘domain’ portion.
%n%{user|username}Referencing the ‘user’ variable, extract only the ‘username’ portion.

Now that I understood the proper syntax, I was able to update the relevant sections of my Dovecot auth directives:

mail_driver = maildir
mail_home = /var/mail/vhosts/%{user|domain}/%{user|username}/
mail_path = /var/mail/vhosts/%{user|domain}/%{user|username}/

userdb passwd-file {
        userdb_driver = passwd-file
        auth_username_format = %{user|username}
        passwd_file_path = /var/mail/vhosts/%{user|domain}/passwd
}

passdb passwd-file {
        passdb_driver = passwd-file
        auth_username_format = %{user|username}
        passwd_file_path = /var/mail/vhosts/%{user|domain}/shadow
}

After restarting Dovecot, I was able to connect via my IMAP clients once again.

Cheers,
Nathan Zachary

Leave a Reply

Your email address will not be published.