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

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.