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
- Instead of having, say,
- Changes to the names of some directives (including SSL)
ssl_cert
becamessl_server_cert_file
ssl_key
becamessl_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
- Note that there are many options for setting the path and home directories
- Instead of having
- 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 toauth_allow_cleartext
and now defaults to no for better security
- The
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
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 variable | New syntax | Explanation |
---|---|---|
%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