Configuring Dovecot with TLS and Sieve Filtering
August 19, 2026
Dovecot is the IMAP server that gives mail clients access to mailboxes and the LMTP server that Postfix uses for local delivery. It handles authentication for both itself and Postfix (via SASL), TLS encryption for client connections, and server-side mail filtering through Sieve. This post covers installing Dovecot on Gentoo with MySQL-backed authentication, TLS configuration, LMTP delivery, and Sieve filtering.
Prerequisites
- Postfix is installed and configured for virtual mailboxes
- MySQL is running with the mailserver database
- TLS certificates are available
Setting USE Flags
cat > /etc/portage/package.use/dovecot << 'EOF'
net-mail/dovecot mysql sieve managesieve lmtp
EOF
Installing Dovecot
emerge -av net-mail/dovecot
Dovecot Configuration
Dovecot's configuration is split across multiple files in /etc/dovecot/. The main file is /etc/dovecot/dovecot.conf, with includes pulling from /etc/dovecot/conf.d/.
Main Configuration
Edit /etc/dovecot/dovecot.conf:
protocols = imap lmtp sieve
listen = *
# Include conf.d directory
!include conf.d/*.conf
Authentication — conf.d/10-auth.conf
disable_plaintext_auth = yes
auth_mechanisms = plain login
# Use SQL-based authentication
!include auth-sql.conf.ext
SQL Authentication — auth-sql.conf.ext
Create /etc/dovecot/auth-sql.conf.ext:
passdb {
driver = sql
args = /etc/dovecot/dovecot-sql.conf.ext
}
userdb {
driver = sql
args = /etc/dovecot/dovecot-sql.conf.ext
}
SQL Connection — dovecot-sql.conf.ext
Create /etc/dovecot/dovecot-sql.conf.ext:
driver = mysql
connect = host=127.0.0.1 dbname=mailserver user=mailuser password=mail-db-password
default_pass_scheme = SHA512-CRYPT
password_query = SELECT username AS user, password, \
'/var/vmail/%d/%n' AS userdb_home, \
5000 AS userdb_uid, 5000 AS userdb_gid \
FROM mailbox WHERE username = '%u' AND active = '1'
user_query = SELECT '/var/vmail/%d/%n' AS home, \
5000 AS uid, 5000 AS gid \
FROM mailbox WHERE username = '%u' AND active = '1'
Set restrictive permissions:
chmod 640 /etc/dovecot/dovecot-sql.conf.ext
chown root:dovecot /etc/dovecot/dovecot-sql.conf.ext
Mail Location — conf.d/10-mail.conf
mail_location = maildir:/var/vmail/%d/%n/Maildir
namespace inbox {
inbox = yes
separator = /
}
mail_uid = 5000
mail_gid = 5000
mail_privileged_group = vmail
The %d expands to the domain and %n to the local part of the email address. A mailbox for user@example.com is stored at /var/vmail/example.com/user/Maildir/.
TLS — conf.d/10-ssl.conf
ssl = required
ssl_cert = </etc/lego/example.com/certificates/mail.example.com.crt
ssl_key = </etc/lego/example.com/certificates/mail.example.com.key
ssl_min_protocol = TLSv1.2
ssl_prefer_server_ciphers = yes
The < prefix tells Dovecot to read the file contents, not use the path as a string. ssl = required means clients must use TLS — plaintext IMAP is not allowed.
LMTP Service — conf.d/20-lmtp.conf
protocol lmtp {
mail_plugins = $mail_plugins sieve
postmaster_address = postmaster@example.com
}
The Sieve plugin is loaded in the LMTP protocol context so that server-side filters run during delivery.
IMAP Protocol — conf.d/20-imap.conf
protocol imap {
mail_max_userip_connections = 20
}
Master Process — conf.d/10-master.conf
This file defines how Dovecot services communicate. The critical sections:
service imap-login {
inet_listener imap {
port = 0 # Disable plaintext IMAP
}
inet_listener imaps {
port = 993
ssl = yes
}
}
service lmtp {
unix_listener /var/spool/postfix/private/dovecot-lmtp {
mode = 0600
user = postfix
group = postfix
}
}
service auth {
unix_listener /var/spool/postfix/private/auth {
mode = 0660
user = postfix
group = postfix
}
unix_listener auth-userdb {
mode = 0660
user = vmail
group = vmail
}
}
service auth-worker {
user = vmail
}
LMTP socket — Postfix connects to Dovecot via a Unix socket in Postfix's chroot directory. This is the virtual_transport = lmtp:unix:private/dovecot-lmtp from the Postfix configuration.
Auth socket — Postfix uses this for SASL authentication on the submission port. When a user logs in on port 587, Postfix delegates the credential check to Dovecot through this socket.
Plaintext IMAP disabled — port 143 is set to 0 (disabled). Only IMAPS on port 993 with TLS is available.
Sieve Filtering
Sieve is a server-side mail filtering language. It runs during LMTP delivery and can sort mail into folders, reject spam, send vacation replies, and more.
Sieve Configuration — conf.d/90-sieve.conf
plugin {
sieve = file:~/sieve;active=~/.dovecot.sieve
sieve_default = /var/vmail/sieve/default.sieve
sieve_default_name = default
sieve_before = /var/vmail/sieve/before.d/
}
sieve — user-specific Sieve scripts are stored in the user's home directory under a sieve folder. The active script is symlinked to .dovecot.sieve.
sieve_default — a global default script that runs if the user has no personal script.
sieve_before — scripts in this directory run before the user's personal script. Use this for system-wide rules like spam filtering.
Creating a Default Sieve Script
Create the directories:
mkdir -p /var/vmail/sieve/before.d
chown -R vmail:vmail /var/vmail/sieve
Create a spam-filing script at /var/vmail/sieve/before.d/spam.sieve:
require ["fileinto", "mailbox"];
if header :contains "X-Spam" "Yes" {
fileinto :create "Junk";
stop;
}
Compile the Sieve script:
sievec /var/vmail/sieve/before.d/spam.sieve
This compiles the human-readable script into a binary format that Dovecot can execute efficiently.
Create a default script at /var/vmail/sieve/default.sieve:
require ["fileinto", "mailbox"];
# Default: deliver to INBOX (no-op, but makes the script explicit)
keep;
sievec /var/vmail/sieve/default.sieve
chown -R vmail:vmail /var/vmail/sieve
Precompiling Global Sieve Scripts
Global Sieve scripts (in sieve_before or sieve_default) must be precompiled because the vmail user typically doesn't have write permission to the directories where global scripts live. Without precompilation, Dovecot tries to compile at runtime and fails silently.
After creating or modifying any global script, compile it:
sievec /var/vmail/sieve/before.d/spam.sieve
sievec /var/vmail/sieve/default.sieve
This creates .svbin files alongside the .sieve files. Dovecot loads the precompiled version if it exists and is newer than the source.
Sieve Filtering Tips
When writing Sieve rules that match email addresses (e.g., filtering by recipient), use address instead of header:
# CORRECT — extracts the email address from the header, ignoring display names
if address :is "to" "user@example.com" {
fileinto "Work";
}
# WRONG — matches the raw header value, fails when display names are present
# e.g., "John Doe <user@example.com>" does not match "user@example.com"
if header :is "to" "user@example.com" {
fileinto "Work";
}
The address test parses the header and extracts just the email address, so it works regardless of whether the sender's client includes a display name. The header test does an exact string match against the raw header value, which almost always includes a display name and will fail to match.
Roundcube's ManageSieve plugin generates header :is by default for some filter types. If a filter isn't matching, check the raw Sieve script and replace header :is with address :is for address-based comparisons.
IMAPSieve — Automatic Spam and Ham Learning
IMAPSieve triggers Sieve scripts when messages are moved between IMAP folders. Combined with Rspamd, this enables automatic Bayes training: moving a message to the Spam folder teaches Rspamd it's spam, and moving it out teaches Rspamd it's ham.
Configure IMAPSieve in the IMAP protocol section:
protocol imap {
mail_plugins = $mail_plugins imap_sieve
}
plugin {
# Trigger when a message is moved INTO Spam
imapsieve_mailbox1_name = Spam
imapsieve_mailbox1_causes = COPY APPEND
imapsieve_mailbox1_before = file:/etc/dovecot/sieve.d/learn-spam.sieve
# Trigger when a message is moved OUT of Spam
imapsieve_mailbox2_name = *
imapsieve_mailbox2_from = Spam
imapsieve_mailbox2_causes = COPY
imapsieve_mailbox2_before = file:/etc/dovecot/sieve.d/learn-ham.sieve
}
Create the learning scripts. /etc/dovecot/sieve.d/learn-spam.sieve:
require ["vnd.dovecot.pipe", "copy", "imapsieve", "environment", "variables"];
if environment :matches "imap.cause" "*" {
set :length "tlen" "${1}";
}
if string "${tlen}" "0" {
stop;
}
pipe :copy "rspamd-learn-spam.sh";
/etc/dovecot/sieve.d/learn-ham.sieve:
require ["vnd.dovecot.pipe", "copy", "imapsieve", "environment", "variables"];
if environment :matches "imap.cause" "*" {
set :length "tlen" "${1}";
}
if string "${tlen}" "0" {
stop;
}
pipe :copy "rspamd-learn-ham.sh";
Create the shell scripts that call rspamc:
cat > /usr/local/bin/rspamd-learn-spam.sh << 'EOF'
#!/bin/bash
exec /usr/bin/rspamc learn_spam
EOF
cat > /usr/local/bin/rspamd-learn-ham.sh << 'EOF'
#!/bin/bash
exec /usr/bin/rspamc learn_ham
EOF
chmod 755 /usr/local/bin/rspamd-learn-spam.sh /usr/local/bin/rspamd-learn-ham.sh
Precompile the IMAPSieve scripts (the vmail user cannot write to /etc/dovecot/sieve.d/):
sievec /etc/dovecot/sieve.d/learn-spam.sieve
sievec /etc/dovecot/sieve.d/learn-ham.sieve
After this, users can train Rspamd simply by dragging messages to or from the Spam folder in their mail client or Roundcube.
Auto-Expunge Old Messages
Spam and Trash folders accumulate messages indefinitely unless cleaned up. Use doveadm expunge via cron to automatically delete old messages:
# Delete Spam older than 90 days (daily at 4:15am)
echo "15 4 * * * root /usr/bin/doveadm expunge -A mailbox Spam savedbefore 90d" >> /etc/crontab
# Delete Trash older than 30 days (daily at 4:30am)
echo "30 4 * * * root /usr/bin/doveadm expunge -A mailbox Trash savedbefore 30d" >> /etc/crontab
The -A flag applies to all users. Adjust the retention periods as needed.
ManageSieve
ManageSieve allows mail clients (like Thunderbird with the Sieve add-on) to upload and manage Sieve scripts remotely:
service managesieve-login {
inet_listener sieve {
port = 4190
}
}
service managesieve {
}
ManageSieve listens on port 4190 and requires the same authentication as IMAP.
Logging Configuration
Configure Dovecot to log authentication attempts and mail operations:
# conf.d/10-logging.conf
log_path = syslog
syslog_facility = mail
auth_verbose = yes
auth_verbose_passwords = no
mail_debug = no
auth_verbose = yes logs every authentication attempt — essential for diagnosing login failures. Set mail_debug = yes temporarily when troubleshooting delivery issues; it's too noisy for production.
Quota Support (Optional)
To enforce mailbox size limits, enable the quota plugin:
# conf.d/90-quota.conf
plugin {
quota = maildir:User quota
quota_grace = 10%%
quota_status_success = DUNNO
quota_status_nouser = DUNNO
quota_status_overquota = "552 5.2.2 Mailbox is full"
}
protocol imap {
mail_plugins = $mail_plugins imap_quota
}
Quotas are set per-user in PostfixAdmin. The quota_grace setting allows users to receive one more message that pushes them 10% over the limit, preventing hard cutoffs on important messages.
Starting and Enabling Dovecot
rc-update add dovecot default
rc-service dovecot start
Verifying the Installation
Check Dovecot Is Listening
ss -lnt sport = :993
ss -lnt sport = :4190
Test IMAP TLS Connection
openssl s_client -connect localhost:993
You should see the TLS handshake followed by Dovecot's IMAP banner:
* OK [CAPABILITY IMAP4rev1 ...] Dovecot ready.
Test LMTP Delivery
Verify the LMTP socket exists:
ls -la /var/spool/postfix/private/dovecot-lmtp
Test SASL Auth Socket
Verify the auth socket exists:
ls -la /var/spool/postfix/private/auth
Test Authentication
doveadm auth test user@example.com password
This tests the full authentication chain — Dovecot queries MySQL, verifies the password, and reports success or failure.
Check Dovecot Configuration
doveconf -n
This outputs the effective configuration with all defaults applied. Review it for any unexpected values.
dovecot -n
This checks for configuration errors and prints a summary.
Restarting Postfix
After Dovecot is running, restart Postfix to pick up the LMTP and auth sockets:
rc-service postfix restart
Summary
After completing these steps:
- Dovecot serves IMAP over TLS on port 993
- LMTP accepts mail from Postfix for local delivery
- Authentication is backed by MySQL (shared with Postfix)
- SASL authentication for Postfix submission is provided via Unix socket
- Sieve filters run during delivery for spam filing and user-defined rules
- ManageSieve allows remote script management on port 4190
- Plaintext connections are disabled — all access requires TLS
The mail server can now receive and store mail. The next steps are DKIM, SPF, and DMARC authentication for outgoing mail and Rspamd for incoming spam filtering.