41 checks per scan34 blacklistsSPF, DKIM, DMARC, MTA-STS, BIMINo signup
← The AcademyIntermediate · 8h · free

Dovecot: IMAP/POP3 at Production Scale

12-module Dovecot course: IMAP/POP3 internals, mdbox vs Maildir, SQL/LDAP auth, Sieve, quotas, replication, director clustering, FTS, and forensics.

12Modules
25Lessons
8hEstimated
$0Cost

Module index

Module 013 lessons

Dovecot Architecture

What Dovecot is, how it fits into a mail stack, and why it's the IMAP/POP3 standard.

01

What Dovecot does (and doesn't)

Dovecot is an IMAP, POP3, ManageSieve, and LMTP server written in C by Timo Sirainen. It is not an MTA — it doesn't relay mail between servers. It receives mail from an MTA (usually Postfix), stores it, and serves it to clients.

Where Dovecot fits in the stack:

Internet → MTA (Postfix) → LMTP → Dovecot → Maildir/mdbox on disk
                                  ↑
                  IMAP/POP3 ← Dovecot ← clients (Outlook, iOS, Thunderbird)

What Dovecot provides:

Service Default port
IMAP 143 (STARTTLS)
IMAPS 993 (implicit TLS)
POP3 110
POP3S 995
ManageSieve 4190
LMTP 24 (or unix socket)
Submission 587 (newer versions)

Why Dovecot dominates:

  • Fastest IMAP server with proper indexing (mdbox > 100k msgs/folder no problem).
  • Modular auth: PAM, passwd-file, MySQL, PostgreSQL, LDAP, OAuth2.
  • Strong replication, proxying, and director clustering.
  • Sieve scripting for server-side filtering.
  • Sane defaults — most production sites tweak <20 settings.
Key takeaways
  • Dovecot stores and serves mail; it doesn't send between servers
  • Postfix typically hands off to Dovecot via LMTP for delivery
  • Ports to remember: 993 IMAPS, 995 POP3S, 4190 ManageSieve
02

Process model and master/worker design

Dovecot uses a master process that spawns specialized workers. This isolates failure: a buggy IMAP session can't crash the auth daemon.

Core processes:

Process Role
dovecot (master) Spawns and supervises children
anvil Tracks per-user/IP connection counts
log Centralized log collector
config Serves config to all other processes
stats In-memory metrics
auth Single auth daemon for all protocols
imap-login / pop3-login Pre-auth network handlers (low privilege)
imap / pop3 Per-session post-auth workers
lmtp Local delivery from MTA
doveadm Admin CLI server

Privilege separation: imap-login runs as user dovenull with no shell. Only after auth succeeds does the session hand off to imap running as the mailbox owner. This is why Dovecot has had so few CVEs over 20 years.

Concurrency: by default each user gets one imap process per session. With service_count = 0, Dovecot reuses processes to lower fork() overhead under high concurrency.

Key takeaways
  • Master/worker design isolates crashes per process
  • Pre-auth handlers run as nobody-user dovenull — strong privilege separation
  • service_count tunes fork() vs memory tradeoff under high concurrency
03

Where everything lives

Standard Dovecot layout on Debian/Ubuntu:

Path Purpose
/etc/dovecot/dovecot.conf Top-level config (mostly !includes)
/etc/dovecot/conf.d/ Modular configs (10-mail.conf, 10-auth.conf, ...)
/etc/dovecot/local.conf Your overrides (load last)
/etc/dovecot/sieve/ Global Sieve scripts
/var/mail/vhosts/ or /home/vmail/ Mailbox storage
/var/log/dovecot.log Service log
/var/log/dovecot-info.log Verbose log (when enabled)
/var/run/dovecot/ Sockets, PIDs

Always edit local.conf for overrides — never edit files in conf.d/ directly. Distro upgrades will clobber them.

Quick config dump:

doveconf -n

Shows the effective config with all includes resolved and defaults stripped. The single most useful command when troubleshooting.

Key takeaways
  • Put all overrides in /etc/dovecot/local.conf — survives upgrades
  • doveconf -n shows the effective config — use it before changing anything
  • Mail storage path is up to you; /var/mail/vhosts/<domain>/<user>/ is common
Module 022 lessons

Installation & Mail Storage Formats

Install Dovecot and choose between Maildir, mdbox, and sdbox.

04

Installing Dovecot on Debian/Ubuntu

Install core + protocol packages:

apt update
apt install dovecot-core dovecot-imapd dovecot-pop3d dovecot-lmtpd \
            dovecot-managesieved dovecot-sieve dovecot-mysql

Package roles:

Package Adds
dovecot-core Master + auth + base config
dovecot-imapd IMAP protocol
dovecot-pop3d POP3 protocol
dovecot-lmtpd LMTP for MTA handoff
dovecot-managesieved Sieve script management (port 4190)
dovecot-sieve Sieve filtering engine
dovecot-mysql / -pgsql / -ldap Auth backend
dovecot-solr Full-text search via Solr

First sanity check:

systemctl status dovecot
doveconf -n | head -30

You should see your protocols listed in protocols = imap pop3 lmtp sieve.

Key takeaways
  • Install per-protocol packages — they're modular
  • doveconf -n confirms what's actually loaded
  • Add dovecot-managesieved if you want user-editable Sieve filters
05

Maildir vs mdbox vs sdbox

Dovecot supports three storage formats. Pick once — migration is possible but painful.

Format Layout Strengths Weaknesses
Maildir 1 file per message Universal, easy to back up file-by-file, every tool understands it Slow on huge folders (100k+ msgs), high inode count
mdbox Many messages per file, sharded Fast, low inode usage, single-instance attachments Dovecot-specific, harder to inspect manually
sdbox 1 message per file, Dovecot-native Like Maildir but with built-in index Less mature than mdbox

Recommendation:

  • <1000 mailboxes, normal usage: Maildir. Simplest backup story.
  • High-volume hosting or huge folders: mdbox. Single-instance attachments alone saves 30–50% disk.
  • New deployments without strong reason: mdbox.

Configure in /etc/dovecot/conf.d/10-mail.conf:

mail_location = maildir:/var/mail/vhosts/%d/%n
# or
mail_location = mdbox:/var/mail/vhosts/%d/%n

%d = domain, %n = user (left side of @).

Key takeaways
  • Maildir is the safest default; mdbox is faster and saves disk
  • Single-instance attachment storage is mdbox-only — huge win at scale
  • Migrate with dsync; don't try rsync between formats
Module 032 lessons

Authentication (PAM, SQL, LDAP, passwd-file)

Configure where Dovecot looks up user credentials and metadata.

06

Auth flow and userdb vs passdb

Dovecot splits authentication into two databases:

  • passdb — verifies the password ("is this person who they say?").
  • userdb — provides user metadata ("where do their messages live, what's their UID, quota?").

Both queries happen on every login. They can come from the same source or different sources (e.g., LDAP for passwords, static template for userdb).

Auth flow:

client → imap-login → auth daemon → passdb lookup → userdb lookup → imap process

If passdb says yes and userdb returns valid metadata, login succeeds. If userdb returns nothing, login fails with "no such user" even if the password was correct.

Enable verbose auth logging when debugging:

auth_verbose = yes
auth_debug = yes

in local.conf. Restart Dovecot. Tail /var/log/dovecot.log and replay the failing login.

Key takeaways
  • passdb checks password; userdb provides UID/GID/home/quota
  • Both must succeed — userdb miss looks like wrong password
  • auth_verbose + auth_debug is the troubleshooting unlock
07

Three common auth backends

1. passwd-file (small static deployments):

/etc/dovecot/users:

alice@example.com:{SHA512-CRYPT}$6$abc...:5000:5000::/var/mail/vhosts/example.com/alice

conf.d/10-auth.conf:

passdb {
  driver = passwd-file
  args = scheme=SHA512-CRYPT username_format=%u /etc/dovecot/users
}
userdb {
  driver = passwd-file
  args = username_format=%u /etc/dovecot/users
}

2. SQL (most hosting setups):

conf.d/auth-sql.conf.ext:

passdb { driver = sql; args = /etc/dovecot/dovecot-sql.conf.ext }
userdb { driver = sql; args = /etc/dovecot/dovecot-sql.conf.ext }

/etc/dovecot/dovecot-sql.conf.ext:

driver = mysql
connect = host=127.0.0.1 dbname=mail user=mail password=secret
default_pass_scheme = SHA512-CRYPT
password_query = SELECT email AS user, password FROM users WHERE email='%u' AND active=1
user_query = SELECT '/var/mail/vhosts/%d/%n' AS home, 5000 AS uid, 5000 AS gid, \
  CONCAT('*:bytes=', quota_bytes) AS quota_rule FROM users WHERE email='%u'

3. LDAP (enterprise): conf.d/auth-ldap.conf.ext with hosts, base, pass_filter, user_filter. Works great with FreeIPA, Samba AD, OpenLDAP.

🎯 Always hash passwords with SHA512-CRYPT or ARGON2I. Never store plaintext, MD5, or unsalted SHA-1.

Key takeaways
  • passwd-file is great for <50 users; SQL scales to thousands
  • SHA512-CRYPT or ARGON2I — nothing weaker
  • Comment out auth backends you don't use; Dovecot tries them in order
Module 042 lessons

SSL/TLS, Certificates, and Ports

Configure modern TLS, Let's Encrypt automation, and the right port for each protocol.

08

Modern TLS settings

Dovecot 2.3+ defaults are reasonable but always pin them explicitly in local.conf:

ssl = required
ssl_min_protocol = TLSv1.2
ssl_cipher_list = ECDHE+AESGCM:ECDHE+CHACHA20:!aNULL:!MD5
ssl_prefer_server_ciphers = yes
ssl_cert = </etc/letsencrypt/live/mail.example.com/fullchain.pem
ssl_key = </etc/letsencrypt/live/mail.example.com/privkey.pem
ssl_dh = </etc/dovecot/dh.pem

Generate a 4096-bit DH parameter file once:

openssl dhparam -out /etc/dovecot/dh.pem 4096

ssl = required means cleartext IMAP/POP3 (143, 110) require STARTTLS before AUTH. Auth attempts in cleartext are refused. For maximum strictness:

disable_plaintext_auth = yes
Key takeaways
  • TLSv1.2+ only; never TLS 1.0/1.1
  • Generate dh.pem once with OpenSSL — 4096-bit
  • disable_plaintext_auth = yes blocks AUTH before STARTTLS
09

Let's Encrypt auto-renewal hook

Certbot renews certs in place but doesn't restart Dovecot. Without a reload, you keep serving the old cert until Dovecot restarts.

Add a deploy hook in /etc/letsencrypt/renewal-hooks/deploy/reload-dovecot.sh:

#!/bin/bash
systemctl reload dovecot
systemctl reload postfix
chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-dovecot.sh

Test certificate from outside:

openssl s_client -connect mail.example.com:993 -servername mail.example.com -showcerts < /dev/null | grep "Verify return code"

Want Verify return code: 0 (ok).

SNI for multiple hostnames: add per-name blocks:

local_name imap.alt.example {
  ssl_cert = </etc/letsencrypt/live/imap.alt.example/fullchain.pem
  ssl_key  = </etc/letsencrypt/live/imap.alt.example/privkey.pem
}
Key takeaways
  • Renewal-hooks/deploy/ runs only on actual cert renewal — efficient
  • Reload Dovecot AND Postfix from the hook
  • local_name { } blocks add SNI without separate IPs
Module 052 lessons

IMAP, POP3, ManageSieve & Submission

Per-protocol tuning and what each one's for.

10

Tuning IMAP for big folders

IMAP performance dies on huge folders (>50k messages). Dovecot's index files keep it sane.

Index location (10-mail.conf):

mail_location = mdbox:/var/mail/vhosts/%d/%n:INDEX=/var/indexes/%d/%n

Putting indexes on fast local SSD while mailboxes live on slower bulk storage is the single best IMAP tuning trick.

Useful per-protocol limits:

service imap-login {
  service_count = 1
  process_min_avail = 4
  vsz_limit = 256M
}
service imap {
  vsz_limit = 1G
  process_limit = 1024
}
  • process_min_avail = 4 keeps 4 idle pre-auth workers ready so login latency is consistent.
  • vsz_limit caps memory per process — protects against pathological clients.

Enable IMAP IDLE forever: imap_idle_notify_interval = 2 mins. Most clients (iOS Mail, Thunderbird) rely on IDLE for push.

Key takeaways
  • Indexes on fast SSD + mailboxes on bulk = best price/performance
  • process_min_avail keeps logins snappy under bursty traffic
  • vsz_limit per process is a kill switch for runaway clients
11

POP3 etiquette and Submission (SMTP)

POP3 in 2026? Yes — IoT devices, scanners, ticketing systems. Dovecot's POP3 is fine; just keep these in mind:

  • pop3_uidl_format = %08Xu%08Xv — required for clients that leave mail on server.
  • Don't mix POP3 + IMAP on the same mailbox without pop3_no_flag_updates = yes or your "Read" flags get rewritten.

Submission (port 587 SMTP-in) — Dovecot 2.3.13+ ships its own submission service that uses the same auth as IMAP. Stops users hammering Postfix's smtpd_sasl.

protocols = imap pop3 lmtp sieve submission
service submission-login {
  inet_listener submission {
    port = 587
  }
}
submission_relay_host = 127.0.0.1
submission_relay_port = 10025

Have Postfix listen on 10025 (no auth required, just accepts from localhost).

ManageSieve (port 4190): lets users upload their own Sieve scripts via Roundcube, Thunderbird, or sieve-connect. Almost free to enable.

Key takeaways
  • Don't mix POP3 and IMAP on the same mailbox without pop3_no_flag_updates
  • Dovecot's submission service centralizes auth across IMAP+SMTP
  • ManageSieve unlocks user-editable filters in webmail clients
Module 062 lessons

Sieve Filtering & Server-Side Rules

Write powerful filters in Sieve that work across every device.

12

Sieve basics and Dovecot extensions

Sieve (RFC 5228) is a small declarative language for mail filtering. Dovecot's Pigeonhole interpreter supports the full standard plus useful extensions.

Example: file by sender + tag urgent:

require ["fileinto", "imap4flags"];

if header :contains "from" "alerts@stripe.com" {
  fileinto "INBOX/Finance";
  setflag "\\Flagged";
  stop;
}

if header :contains "subject" "[URGENT]" {
  setflag "\\Flagged";
}

if address :is "to" "newsletter@example.com" {
  fileinto "INBOX/Newsletters";
}

Useful Dovecot Sieve extensions:

Extension Purpose
fileinto Move to specific folder
imap4flags Set/unset flags
vacation Auto-responder
mailbox Auto-create destination folders
spamtest / spamtestplus Read Rspamd score
editheader Add/remove headers
extprograms Pipe to external scripts
Key takeaways
  • Sieve is the standard — works server-side across every client
  • require { } block must list every extension you use
  • Combine with Rspamd's X-Spam-Score for content-aware routing
13

Global vs per-user Sieve scripts

Global scripts run for every user. Per-user scripts are uploaded via ManageSieve.

conf.d/90-sieve.conf:

plugin {
  sieve = file:~/sieve;active=~/.dovecot.sieve
  sieve_before = /etc/dovecot/sieve/before.sieve
  sieve_after  = /etc/dovecot/sieve/after.sieve
  sieve_dir = ~/sieve
  sieve_extensions = +editheader +mailbox +spamtest
}

Execution order: sieve_before → user script → sieve_after.

Common global rules in before.sieve:

require ["fileinto", "spamtest", "spamtestplus"];

if spamtest :percent :value "ge" "75" {
  fileinto :create "Junk";
  stop;
}

Compile after editing:

sievec /etc/dovecot/sieve/before.sieve

🎯 Always test new global rules with one mailbox first — a broken before.sieve breaks delivery for everyone.

Key takeaways
  • sieve_before runs globally before user rules — Junk routing belongs here
  • sieve_after handles cleanup like adding common headers
  • Always sievec compile and test on one mailbox before rolling out
Module 072 lessons

Quotas, Namespaces & Shared Folders

Disk-space enforcement, public folders, and team mailboxes.

14

Quota plugin and warnings

Enable in 90-quota.conf:

plugin {
  quota = maildir:User quota
  quota_status_success = DUNNO
  quota_status_nouser = DUNNO
  quota_status_overquota = "552 5.2.2 Mailbox is full"
}

protocol lmtp {
  postmaster_address = postmaster@example.com
  mail_plugins = $mail_plugins quota sieve
}

protocol imap {
  mail_plugins = $mail_plugins imap_quota
}

Per-user quota from userdb quota_rule (e.g. *:bytes=5G).

Warning emails at 80% / 95% via the quota-warning service:

plugin {
  quota_warning  = storage=80%% quota-warning 80 %u
  quota_warning2 = storage=95%% quota-warning 95 %u
}
service quota-warning {
  executable = script /usr/local/bin/quota-warning.sh
  unix_listener quota-warning {
    user = vmail
  }
}

quota-warning.sh runs mail to send the user a notice.

Key takeaways
  • Per-user quotas come from userdb, default from plugin config
  • Warn at 80% so users have time to clean up before hard cap
  • 552 over-quota is the right SMTP code for mailbox full
15

Shared mailboxes and public namespaces

Three namespace types: private (each user's own), shared (other users' mailboxes), public (everyone sees the same folders).

Public namespace in 10-mail.conf:

namespace public {
  type = public
  separator = /
  prefix = Public/
  location = maildir:/var/mail/vhosts/public:INDEXPVT=~/public-index
  list = yes
  subscriptions = no
}

Shared namespace (folders other users have ACL'd to you):

namespace {
  type = shared
  separator = /
  prefix = Shared/%%u/
  location = maildir:%%h/Maildir:INDEX=~/shared/%%u
  subscriptions = no
  list = children
}

Grant access via the ACL plugin (90-acl.conf + dovecot-acl file in the source mailbox):

user=bob@example.com lrws

Bob now sees the folder under Shared/alice@example.com/ in his client.

Common use: a support@ mailbox shared with the whole team — replies thread cleanly because everyone reads/writes the same store.

Key takeaways
  • Public namespace = company-wide folders everyone sees
  • Shared namespace + ACL = team inboxes done right
  • Avoid alias-fan-out for team mail — threading breaks
Module 082 lessons

Replication & Multi-Master Setups

Active/active mailbox replication with dsync.

16

How dsync replication works

Dovecot's dsync synchronizes mailboxes between two hosts at the protocol level (not file rsync). It understands IMAP state — flags, keywords, expunges, GUIDs.

Modes:

Mode Trigger Use case
replicator (background) Live, event-driven Active/active production
dsync (CLI) One-shot or cron Migrations, periodic backup

Architecture for active/active:

[ Client ] ─┬─ [ Dovecot A ] ⇄ replication ⇄ [ Dovecot B ] ─┬─ [ Client ]
            └─ load balancer (director)                      ─┘

Both nodes accept reads and writes; the replicator daemon syncs deltas continuously. Conflict resolution uses GUIDs (Globally Unique Mailbox IDs) so the same message stays the same message on both sides.

Key takeaways
  • dsync replicates IMAP state, not bytes — flags and GUIDs stay coherent
  • replicator daemon does live continuous sync
  • Pair with director for true active/active
17

Configuring two-node replication

On both nodes, in local.conf:

mail_plugins = $mail_plugins notify replication

service replicator {
  process_min_avail = 1
  unix_listener replicator-doveadm {
    mode = 0600
    user = vmail
  }
}

service aggregator {
  fifo_listener replication-notify-fifo {
    user = vmail
  }
  unix_listener replication-notify {
    user = vmail
  }
}

doveadm_password = "very-long-shared-secret"
doveadm_port = 12345

plugin {
  mail_replica = tcp:other-node.example.com:12345
}

Open port 12345 between the two hosts. Restart both. Trigger a full sync the first time:

doveadm replicator replicate -f '*'

Monitor:

doveadm replicator status '*'

Queued should drop to 0 quickly. Failed > 0 means look at /var/log/dovecot.log on both nodes.

Key takeaways
  • Shared doveadm_password gates both nodes — keep it long
  • doveadm replicator status '*' is the live progress view
  • First full sync can take hours on large mailstores — run off-peak
Module 092 lessons

Performance Tuning & Indexes

Where to spend SSD, when to use Solr, and what limits actually matter.

18

Index, cache, and storage placement

Dovecot's performance ceiling is almost always IOPS on the index files, not throughput on the mail store.

Three layers, three storage tiers:

Data Access pattern Best storage
Indexes (dovecot.index*) Random read/write, small NVMe local SSD
Mail bodies (mdbox/Maildir) Mostly sequential read Bulk SSD or even spinning disk
Full-text search Append-only writes, random reads SSD

Config:

mail_location = mdbox:/var/mail/vhosts/%d/%n:INDEX=/nvme/indexes/%d/%n:CONTROL=/nvme/control/%d/%n

Memory cache (RAM is fastest):

mail_cache_min_mail_count = 5
mail_cache_purge_min_size = 32k

Process limits to monitor:

Knob Watch when
process_limit per service Hitting login latency under bursts
mail_max_userip_connections Misbehaving clients holding hundreds of connections
first_valid_uid / last_valid_uid Multi-tenant safety boundary
Key takeaways
  • Put indexes on NVMe — biggest single performance win
  • Bulk storage for bodies; NVMe for indexes and controls
  • Monitor process_limit before increasing it — usually a client bug
19

Full-text search with Solr

IMAP SEARCH BODY on Maildir is O(mailbox size). For users with 10k+ messages, it's brutal.

Solr backend turns body search into indexed lookups (<200 ms across 1M messages).

Install:

apt install dovecot-solr solr-tomcat

Configure in 90-plugin.conf:

mail_plugins = $mail_plugins fts fts_solr
plugin {
  fts = solr
  fts_solr = url=http://127.0.0.1:8983/solr/dovecot/
  fts_autoindex = yes
  fts_autoindex_exclude = \\Trash
}

Bulk index existing mailboxes:

doveadm fts rescan -A

Alternatives:

  • Xapian — embedded, no Java, single-machine only. Use for <500 users.
  • Manticore — newer Lucene fork, faster cold-cache. Less mature Dovecot support.

🎯 For most deployments under 100 mailboxes, you don't need FTS. Above that, it transforms search UX.

Key takeaways
  • FTS turns IMAP SEARCH BODY from O(n) to O(log n)
  • Solr is the mature choice; Xapian is the simple-single-host alternative
  • Exclude Trash and Spam from indexes — saves 30%+ index size
Module 102 lessons

Director, Proxying & Horizontal Scaling

Keep one user pinned to one backend with the Dovecot director.

20

Why you need a director

Plain round-robin load balancing breaks IMAP. Two backends serving the same user produce conflicting indexes, missing messages, and UIDVALIDITY mismatches that re-download whole mailboxes.

Director's job: map each user to exactly one backend at a time, with sticky failover.

[ Client ] → [ Director cluster ] → consistent-hashes user → [ Backend N ]

Two deployment shapes:

  1. Director-only proxy: Director runs on dedicated hosts, proxies IMAP/POP3 to backends.
  2. Director-on-backend: Each backend runs director. Simpler, fewer moving parts.

Most production setups choose #2.

Required pieces:

  • All backends share the same auth (passdb returns the same userdb).
  • All backends mount the same mail storage (NFS, GlusterFS, Ceph, or replication).
  • Directors cluster among themselves via director_servers.
Key takeaways
  • Round-robin LB breaks IMAP — director pins users to a backend
  • Director-on-backend is simpler than separate director hosts
  • Shared storage or dsync replication is required underneath
21

Director configuration

conf.d/10-director.conf:

service director {
  unix_listener login/director {
    mode = 0666
  }
  inet_listener {
    port = 9090
  }
}

director_servers = node1.example.com node2.example.com node3.example.com
director_mail_servers = backend1 backend2 backend3
director_user_expire = 15 min
director_username_hash = %u

Tell IMAP/POP3 login to use it:

service imap-login {
  executable = imap-login director
}
service pop3-login {
  executable = pop3-login director
}

Operations:

doveadm director status              # Cluster view
doveadm director map                 # Active user → backend mappings
doveadm director move alice@e.com backend2   # Force a user to a specific backend

Failover: if a backend disappears, director redirects users to the next healthy one in the hash ring. After 15 minutes of idle (director_user_expire), users can be remapped if the cluster has rebalanced.

Key takeaways
  • director_user_expire controls how sticky a mapping is — 15 min is sane
  • doveadm director move forces a user to a specific backend (maintenance)
  • Hash on %u (username) — never %i (IP), which breaks mobile clients
Module 112 lessons

Security Hardening & Audit Logging

Beyond TLS: rate limits, mailbox quotas, login alerts, audit trails.

22

Anvil and connection limits

anvil tracks per-user and per-IP connection counts so a single malicious or buggy client can't exhaust your process pool.

Recommended limits in local.conf:

mail_max_userip_connections = 20

20 concurrent IMAP connections per (user, IP) pair. Most clients use 1–5; 20 catches bugs without breaking mail.

Per-service caps:

service imap-login {
  process_limit = 1000
  service_count = 1
}
service imap {
  process_limit = 1024
}

Watch the metrics:

doveadm stats dump | grep "auth"

Sustained auth failures from a single IP are usually credential stuffing — feed those IPs to fail2ban.

Key takeaways
  • mail_max_userip_connections = 20 is a great default safety net
  • process_limit per service stops fork bombs
  • Pipe auth-failure logs into fail2ban for automatic banning
23

Login logging and audit trails

10-logging.conf controls verbosity:

log_path = /var/log/dovecot.log
info_log_path = /var/log/dovecot-info.log
debug_log_path =

login_log_format = "%u: %m"
login_log_format_elements = "user=<%u> method=%m rip=%r lip=%l mpid=%e %c"

mail_log_events = delete undelete expunge copy mailbox_delete mailbox_rename
plugin {
  mail_log_fields = uid box msgid size
}

The mail_log plugin (load via mail_plugins = mail_log notify) writes audit lines for destructive events. Critical for compliance (HIPAA, financial services).

Forward to SIEM: use rsyslog or vector to push /var/log/dovecot.log into Loki, Elastic, Splunk. Tag by mpid (mail process ID) to thread events per session.

Track logins from new IPs: combine mail_log with a fail2ban filter or a custom script that emails users when a login arrives from an unrecognized ASN.

Key takeaways
  • mail_log plugin gives you the audit trail compliance requires
  • Forward to SIEM for retention and correlation
  • Tag log events with mpid to thread per session
Module 122 lessons

Troubleshooting, Forensics & Common Errors

How to diagnose Dovecot when something stops working.

24

The five-minute diagnostic ritual

Don't restart Dovecot first. Read first.

1. What's the user-visible symptom? "Can't log in", "messages disappeared", "slow"?

2. Check service status:

systemctl status dovecot
doveconf -n | head -50

3. Tail the right log with the user's pid:

tail -F /var/log/dovecot.log /var/log/dovecot-info.log | grep alice@example.com

4. Replay the failing operation while watching the log. Most issues print a clear error within 5 seconds.

5. Inspect mailbox state:

doveadm mailbox status -u alice@example.com all INBOX
doveadm fetch -u alice@example.com 'uid hdr.subject' mailbox INBOX last 5

doveadm is your Swiss Army knife — read its man page once and refer back forever.

Key takeaways
  • doveconf -n + tail log + grep user = 90% of diagnoses in 2 minutes
  • Never restart before reading — restarts hide root causes
  • doveadm fetch/mailbox/quota are the introspection workhorses
25

Top 10 errors and their fixes

Error Cause Fix
auth failed (right password) userdb miss Check user_query, verify mailbox path exists
Mailbox doesn't exist mail_location wrong doveconf -n mail_location and inspect path
UIDVALIDITY changed Mailbox recreated or replication conflict Check dsync logs; restore from backup if data lost
Permission denied: dovecot.index.log Wrong ownership on index/control chown -R vmail:vmail /var/indexes
Quota exceeded Quota cap hit doveadm quota get -u user@d.com; raise quota_rule
SSL handshake failure Cert expired or wrong path openssl s_client; check renewal hook fired
Connection refused 4190 ManageSieve disabled Enable sieve in protocols = list
LMTP timeout Postfix → Dovecot socket Check /var/spool/postfix/private/dovecot-lmtp perms
Out of memory (vsz_limit) Pathological client Raise vsz_limit or kill the client connection
fts: indexing failed Solr down / disk full systemctl status solr; df -h

Master tool list:

  • doveconf -n — effective config
  • doveadm mailbox status -u <user> — mailbox state
  • doveadm fetch — read messages without IMAP
  • doveadm log find — show recent log files
  • doveadm stats dump — live metrics
  • doveadm director status — cluster view (with director)
Key takeaways
  • UIDVALIDITY changes always mean trouble — investigate immediately
  • Index ownership issues are a top recurring class — vmail:vmail everywhere
  • doveadm fetch is the I-don't-care-about-IMAP backdoor for support

Now check it against your own domain

The course explains the theory. The scan tells you which parts you have actually got right.

Run a free scan