# THIS FILE IS CONTROLLED BY ANSIBLE - DO NOT CHANGE IN DEPLOYMENT! # dovecot config # Note: debian has some settings in conf.d/* that we cannot override # (e.g. we cannot unset passdb pam), so we must disable inclusion of conf.d/* # in dovecot.conf # execution mail_uid = mailstore mail_gid = mailstore mail_privileged_group = mailstore default_vsz_limit = 512M # tls ssl = required ssl_server { cert_file = /etc/dovecot/private/dovecot.pem key_file = /etc/dovecot/private/dovecot.key } ssl_cipher_list = ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384 # mail storage mail_driver = maildir mail_home = /srv/mailstore/%{user | domain}/%{user | username }/ mail_path = /srv/mailstore/%{user | domain}/%{user | username }/Maildir mail_inbox_path = /srv/mailstore/%{user | domain}/%{user | username }/Maildir mailbox_list_layout = fs # auth auth_verbose = yes auth_mechanisms { plain = yes login = yes } auth_verbose = yes auth_default_domain = {{ mailserver.dovecot.auth_default_realm }} # auth service for postfix service auth { unix_listener auth-userdb { } unix_listener /var/spool/postfix/private/auth { mode = 0660 user = postfix group = postfix } } service auth-worker { } # users and passwords: in postgresql pgsql localhost { parameters { port = {{ mailserver.postgresql.port }} user = {{ mailserver.postgresql.username }} password = {{ mailserver.postgresql.password }} dbname = {{ mailserver.postgresql.dbname }} } } sql_driver = pgsql passdb sql { driver = sql default_password_scheme = PBKDF2 query = SELECT \ users.username as user, \ domains.name as domain, \ password, \ suspend_imap_reason as nologin \ FROM users JOIN domains ON users.domain_id=domains.id \ WHERE users.username = '%{user | username}' AND domains.name = '%{user | domain }' } passdb_default_password_scheme = PBKDF2 userdb sql { query = SELECT \ '/srv/mailstore/' || domains.name || '/' || users.username || '/Maildir/' as home, \ 'mailstore' as uid, \ 'mailstore' as gid, \ CONCAT(quota_storage_bytes, 'B') AS quota_storage_size, \ quota_inbox_messages AS quota_message_count \ FROM users JOIN domains ON users.domain_id=domains.id \ WHERE users.username = '%{user | username}' AND domains.name = '%{user | domain}' iterate_query = SELECT \ users.username as username, \ domains.name as domain \ FROM users JOIN domains ON users.domain_id=domains.id \ ORDER BY 2,1 } # quota mail_plugins { quota = yes } quota_storage_size = 20G namespace inbox { mailbox Trash { quota_storage_extra = 100M } } protocol !indexer-worker { mail_vsize_bg_after_count = 100 } protocol imap { mail_plugins { imap_quota = yes } } quota "user-quota" { storage_grace = 200M status_success = DUNNO status_nouser = DUNNO status_overquota = "552 5.2.2 Mailbox is full" exceeded_message = "User %{user} is over quota and must reduce the overall mail volume and/or the number of messages in INBOX." warning warn-95 { quota_storage_percentage = 95 execute quota-warning { args = 95 %{user} } } warning warn-80 { quota_storage_percentage = 80 execute quota-warning { args = 80 %{user} } } warning warn-under { quota_storage_percentage = 95 threshold = under execute quota-warning { args = below %{user} } } } service quota-warning { executable = script /usr/local/bin/quota-warning.sh unix_listener quota-warning { user = dovenull group = dovenull mode = 0660 } } service quota-status { executable = quota-status -p postfix #unix_listener /var/spool/postfix/private/quota-status { # user = postfix #} inet_listener quota-status { port = 12480 } client_limit = 1 } # check: # doveadm quota get -A # echo -e "recipient=USER@DOMAIN\n" | nc -v 127.0.0.1 12480 # namespaces namespace inbox { type = private inbox = yes mail_driver = maildir separator = / mail_path = /srv/mailstore/%{user | domain}/%{user | username}/Maildir mailbox_list_layout = fs # location = maildir:/srv/mailstore/%%d/%%n/Maildir:LAYOUT=fs:INDEXPVT=~/shared/%%d/%%n/ subscriptions = yes list = yes mailbox Drafts { special_use = \Drafts } mailbox Junk { auto = subscribe special_use = \Junk autoexpunge = 180d } mailbox Trash { auto = subscribe special_use = \Trash autoexpunge = 180d } mailbox Sent { special_use = \Sent } mailbox "Sent Messages" { special_use = \Sent } } namespace shared { type = shared inbox = no mail_driver = maildir separator = / prefix = shared/$user/ #prefix = shared/$domain/$username/ mail_path = /srv/mailstore/%{owner_user | domain}/%{owner_user | username}/Maildir #mail_index_path = ~/shared/%{owner_user | domain}/%{owner_user | username}/ mail_index_private_path = ~/shared/%{owner_user | domain}/%{owner_user | username}/ mailbox_list_layout = fs subscriptions = no list = yes } #namespace roles { # type = shared # inbox = no # mail_driver = maildir # separator = / # prefix = roles/ # mail_path = /srv/mailstore/role_specific/roles/Maildir # mailbox_list_layout = fs # mail_index_private_path = ~/role_specific/roles/ # subscriptions = no # list = yes #} #namespace virtual { # separator = / # prefix = virtual/ # mail_path = virtual:/srv/mailstore/%{user | domain}/%{user | username}/Maildir_virtual #} # imap service service imap { } service imap-login { inet_listener imap { type = imap } inet_listener imaps { type = imaps } } # lmtp service service lmtp { unix_listener /var/spool/postfix/private/dovecot-lmtp { group = postfix mode = 0600 user = postfix } } protocol lmtp { postmaster_address = {{ mailserver.dovecot.postmaster_email }} mail_plugins { quota = yes } auth_username_format = %{user | username |lower}@%{user | domain | lower} } # acl mail_plugins { acl = yes } acl_driver = vfile protocol imap { mail_plugins { imap_acl = yes } } acl_sharing_map { dict proxy { name = acl } } imap_acl_allow_anyone = yes dict_server { dict acl { driver = sql sql_driver = pgsql pgsql localhost { parameters { port = {{ mailserver.postgresql.port }} user = {{ mailserver.postgresql.username }} password = {{ mailserver.postgresql.password }} dbname = {{ mailserver.postgresql.dbname }} } } dict_map shared/shared-boxes/user/$to/$from { sql_table = shared_folders value_field dummy { } key_field from_user { value = $from } key_field to_user { value = $to } } dict_map shared/shared-user-boxes-rev/$from/$to { sql_table = shared_folders value_field dummy { } key_field from_user { value = $from } key_field to_user { value = $to } } dict_map shared/shared-boxes/anyone/$from { sql_table = shared_folders_anyone value_field dummy { } key_field from_user { value = $from } } } } service dict { unix_listener dict { mode = 0660 user = dovecot group = mailstore } } # TODO enable in v2.4.2 #acl_dict_index = yes # debugging: # doveadm acl debug -u ACCESSING_USER@DOMAIN shared/SHARING_USER@DOMAIN/FOLDERNAME # fulltext search mail_plugins { fts = yes fts_flatcurve = yes } fts_autoindex = yes #fts_autoindex_max_recent_msgs = 0 fts_search_add_missing = yes language_filters = normalizer-icu snowball stopwords language_tokenizers = generic email-address language_tokenizer_generic_algorithm = simple # Note: the 'language' settings is set mandatory by dovecot but has totally NO impact on FTS flatcurve module language en { default = yes filters = lowercase snowball english-possessive stopwords } language de { } fts flatcurve { substring_search = yes } # cf. /usr/share/doc/dovecot-fts-xapian/README.md.gz fts flatcurve { driver = flatcurve autoindex = yes } mailbox Trash { special_use = \Trash fts_autoindex = no } mailbox Junk { special_use = \Junk fts_autoindex = no } fts_header_excludes { * = yes } fts_header_includes { From = yes To = yes Cc = yes Bcc = yes Subject = yes Message-ID = yes Date = yes } # sieve plugin # Note: we put sieve scripts below /var/lib/dovecot/sieve/ rather than /etc, # because dovecot may want to recompile *.svbin and writing under /etc is prohobited # by systemd setting ProtectSystem=full protocols { sieve = yes } protocol sieve { } protocol lmtp { mail_plugins { sieve = yes } } sieve_script personal { type = personal active_path = /srv/mailstore/%{user | domain}/%{user | username }/.dovecot.sieve path = /srv/mailstore/%{user | domain}/%{user | username }/sieve } sieve_script before { type = before sieve_script_path = /var/lib/dovecot/sieve/before } sieve_script after { type = after sieve_script_path = /var/lib/dovecot/sieve/after } #sieve_user_log_path = /srv/mailstore/%{user | domain}/%{user | username }/.dovecot.sieve.log service managesieve { } service managesieve-login { inet_listener sieve { port = 4190 } } # sieve-extprograms plugin sieve_plugins { sieve_extprograms = yes } sieve_global_extensions { vnd.dovecot.pipe = yes vnd.dovecot.filter = yes vnd.dovecot.execute = yes vnd.dovecot.debug = yes } sieve_pipe_bin_dir = /var/lib/dovecot/sieve/pipes sieve_execute_bin_dir = /var/lib/dovecot/sieve/pipes # imap-sieve plugin protocol imap { mail_plugins { imap_sieve = yes } } sieve_plugins { sieve_imapsieve = yes } mailbox Junk { sieve_script learn-spam { type = before cause = copy path = /var/lib/dovecot/sieve/pipes/learn-spam.sieve } } imapsieve_from Junk { sieve_script learn-ham { type = before cause = copy path = /var/lib/dovecot/sieve/pipes/learn-ham.sieve } } # (disabled) service health check # cf. https://doc.dovecot.org/main/core/config/health_check.html #service health-check { # executable = script -p health-check.sh # inet_listener health-check { # port = 5001 # } #} #log_debug = category=auth #log_debug = category=imap #log_debug = category=lmtp #log_debug = category=fts #log_debug = category=sieve #log_debug = category=imapsieve #log_debug = category=acl #log_debug = category=dict_server #log_debug = category=namespace #log_debug = category=mail # More info: # doveadm -v service status # See also # * https://monospace.games/posts/20250815-dovecot-24.html # * https://workaround.org/ispmail-trixie/ # * https://thomas-leister.de/en/mailserver-migrate-config-to-dovecot-2.4-debian-trixie/