Last active
January 26, 2026 21:35
-
-
Save dmckeone/7cdf170624d77c4ddcdce1721c6f760e to your computer and use it in GitHub Desktop.
Dovecot 2.4 primary server configuration on Ubuntu 24.04 with LDAP (LLDAP), rspamd education, and Full Text Search (fts_flatcurve)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Dovecot 2.4.2 primary server w/ LDAP (lldap), rspamd education, and Full Text Search (fts_flatcurve) | |
| # | |
| # This configuration is intended to be used with Jinja2, so all variables look like: {{ variable }} | |
| # | |
| # For gateway/relay see: https://gist.github.com/dmckeone/20b09040909fb010ae97f1139e2cf827 | |
| # | |
| # On Ubuntu 24.04 LTS this requires the PPA: https://repo.dovecot.org/ | |
| # | |
| # Generic settings reference: https://doc.dovecot.org/2.4.2/core/summaries/settings.html | |
| # | |
| # Example Configs: https://source.willem.com/dovecot-2.4-sample-config/conf.d/ | |
| # | |
| # Unlike a stock install, other files are not read (eg. conf.d/*) | |
| dovecot_config_version = 2.4.2 | |
| dovecot_storage_version = 2.4.2 | |
| log_path = /var/log/dovecot.log | |
| auth_mechanisms = plain login | |
| auth_username_format = %{user | lower} | |
| # LDAP Connection Settings | |
| ldap_version = 3 | |
| ldap_uris = ldaps://{{ mail_ldap_server }} # e.g server.example.com | |
| ldap_base = ou=people,{{ ldap_base_dn }} # e.g dc=example,dc=com | |
| ldap_auth_dn = {{ ldap_bind_dn }} # e.g uid=admin,ou=people,dc=example,dc=com | |
| ldap_auth_dn_password = {{ ldap_bind_pw }} # password for admin user | |
| # useful commands for testing | |
| # doveadm auth test user@example.com password | |
| # doveadm auth lookup user@example.com | |
| # doveadm user user@example.com | |
| # https://doc.dovecot.org/2.4.2/core/config/auth/databases/ldap.html#worker-processes | |
| passdb_use_worker = yes | |
| userdb_use_worker = yes | |
| # Use LDAP as the password database (passdb) | |
| passdb ldap { | |
| driver = ldap | |
| # log into LDAP server using the username and password given by client. | |
| ldap_bind = yes | |
| ldap_bind_userdn = uid=%{user | username | lower},ou=people,{{ ldap_base_dn }} | |
| # only allow user@example.com to login, not an alias | |
| # mail check makes sure user supplies entire email address and not just username | |
| # uid check makes sure that user isn't trying to login with an alias | |
| # | |
| # WORKAROUND: mailaliasN is a custom set of LDAP user attributes that allow for searchable aliases: | |
| # https://github.com/lldap/lldap/issues/997 | |
| # | |
| # DEV NOTE: mailenabled is a custom LDAP user attribute | |
| filter = (&(uid=%{user | username | lower})(objectclass=mailAccount)(mailenabled=1)(|(mail=%{user | lower}){% for i in range(1, ldap_max_aliases + 1) %}(mailalias{{ i }}=%{user | lower}){% endfor %})) | |
| } | |
| userdb ldap { | |
| driver = ldap | |
| # WORKAROUND: mailaliasN is a custom set of LDAP user attributes that allow for searchable aliases: | |
| # https://github.com/lldap/lldap/issues/997 | |
| # | |
| # DEV NOTE: mailenabled is a custom LDAP user attribute | |
| filter = (&(objectclass=mailAccount)(mailenabled=1)(|(mail=%{user | lower}){% for i in range(1, ldap_max_aliases + 1) %}(mailalias{{ i }}=%{user | lower}){% endfor %})) | |
| fields { | |
| user = %{ldap:mail} | |
| } | |
| } | |
| # Setup mail storage for virtual user (vmail) configuration | |
| # | |
| # The vmail user and vmail group must be setup on the system prior to starting Dovecot | |
| # | |
| mail_home = /var/mail/%{user | domain}/%{user | username} | |
| mail_driver = maildir | |
| mail_path = ~/mail | |
| mail_uid = vmail | |
| mail_gid = vmail | |
| mail_privileged_group = vmail | |
| # Don't modify message content - this can negatively affect encrypted S/MIME emails | |
| mail_save_crlf = no | |
| # https://doc.dovecot.org/2.4.2/core/config/namespaces.html#example | |
| namespace inbox { | |
| inbox = yes | |
| mailbox Drafts { | |
| special_use = \Drafts | |
| } | |
| mailbox Sent { | |
| auto = subscribe # autocreate and autosubscribe the Sent mailbox | |
| special_use = \Sent | |
| } | |
| mailbox "Sent Messages" { | |
| special_use = \Sent | |
| } | |
| mailbox Junk { | |
| auto = create # autocreate Junk, but don't autosubscribe | |
| special_use = \Junk | |
| fts_autoindex = no | |
| } | |
| mailbox Trash { | |
| special_use = \Trash | |
| fts_autoindex = no | |
| } | |
| } | |
| protocols { | |
| imap = yes | |
| lmtp = yes | |
| sieve = yes | |
| } | |
| service auth-worker { | |
| # https://doc.dovecot.org/2.4.2/core/config/service.html#auth-worker | |
| user = $SET:default_internal_user | |
| } | |
| # Allow postfix to authenticate against dovecot | |
| service auth { | |
| unix_listener /var/spool/postfix/private/auth { | |
| group = postfix | |
| mode = 0666 | |
| user = postfix | |
| } | |
| unix_listener auth-userdb { | |
| mode = 0666 | |
| } | |
| } | |
| # Create secure IMAPS-only server (requires functional TLS Certificates) | |
| service imap-login { | |
| inet_listener imap { | |
| # Change to 143 if you'd like to enable insecure IMAP | |
| port = 0 | |
| } | |
| inet_listener imaps { | |
| port = 993 | |
| ssl = yes | |
| } | |
| } | |
| # LMTP Receives Incoming Email from Postfix | |
| # https://doc.dovecot.org/2.4.2/core/config/delivery/lmtp.html | |
| service lmtp { | |
| unix_listener /var/spool/postfix/private/dovecot-lmtp { | |
| group = postfix | |
| mode = 0600 | |
| user = postfix | |
| } | |
| } | |
| # https://doc.dovecot.org/2.4.2/core/config/sieve/managesieve.html#managesieve-server | |
| service managesieve { | |
| } | |
| service managesieve-login { | |
| inet_listener sieve { | |
| port = 4190 | |
| } | |
| } | |
| protocol sieve { | |
| managesieve_max_line_length = 65536 | |
| managesieve_logout_format = bytes=%{input}/%{output} | |
| } | |
| # Rspamd support for spamtest filtering | |
| sieve_extensions { | |
| spamtest = yes | |
| spamtestplus = yes | |
| } | |
| sieve_spamtest_status_type = score | |
| # Rspamd output looks like: X-Spamd-Result: default: False [12.00 / 15.00] | |
| sieve_spamtest_status_header = X-Spamd-Result:[[:space:]]+default:[[:space:]]+[[:alnum:]]+[[:space:]]\[(-?[[:digit:]]+\.[[:digit:]]+)[[:space:]]+\/[[:space:]]+-?[[:digit:]]+\.[[:digit:]]+\]; | |
| sieve_spamtest_score_max_header = X-Spamd-Result:[[:space:]]+default:[[:space:]]+[[:alnum:]]+[[:space:]]\[-?[[:digit:]]+\.[[:digit:]]+[[:space:]]+\/[[:space:]]+(-?[[:digit:]]+\.[[:digit:]]+)\]; | |
| protocol lmtp { | |
| postmaster_address = {{ postmaster_email }} # e.g. postmaster@example.com | |
| hostname = {{ bounce_host }} # e.g. mail.example.com | |
| mail_plugins { | |
| sieve = yes | |
| } | |
| } | |
| protocol imap { | |
| mail_max_userip_connections = 30 | |
| mail_plugins { | |
| imap_sieve = yes | |
| } | |
| } | |
| # Storage Compression | |
| # https://doc.dovecot.org/2.4.2/core/plugins/mail_compress.html#mail-compression-plugin-mail-compress | |
| mail_plugins { | |
| mail_compress = yes | |
| } | |
| # Compress with ztd | |
| mail_compress_write_method = zstd | |
| # Full Text Search | |
| # https://doc.dovecot.org/2.4.2/core/plugins/fts_flatcurve.html#configuration-example | |
| mail_plugins { | |
| fts = yes | |
| fts_flatcurve = yes | |
| } | |
| fts_autoindex = yes | |
| fts_autoindex_max_recent_msgs = 999 | |
| fts_search_add_missing = yes | |
| language_filters = normalizer-icu snowball stopwords | |
| language_tokenizers = generic email-address | |
| language_tokenizer_generic_algorithm = simple | |
| language en { | |
| default = yes | |
| filters = lowercase snowball english-possessive stopwords | |
| } | |
| fts flatcurve { | |
| substring_search = yes | |
| } | |
| sieve_plugins { | |
| sieve_imapsieve = yes | |
| sieve_extprograms = yes | |
| } | |
| sieve_global_extensions { | |
| vnd.dovecot.pipe = yes | |
| } | |
| # Specify which programs are allowed to run from sieves (eg rspamc for spam training) | |
| # If using Rpsamd then: ln -s /usr/bin/rspamc /etc/dovecot/sieve-pipe/rspamc | |
| sieve_pipe_bin_dir = /etc/dovecot/sieve-pipe | |
| # Directory for before scripts (filtering) | |
| # Auto-Spam filing: https://gist.github.com/dmckeone/b321fa81a37fe902013aa476749f5a26 | |
| sieve_script before { | |
| sieve_script_type = before | |
| path = /etc/dovecot/sieve/before.d | |
| } | |
| # Directory for after scripts (default rules for filing after arrival) | |
| sieve_script after { | |
| sieve_script_type = before | |
| path = /etc/dovecot/sieve/after.d | |
| } | |
| # User's personal sieve scripts (e.g Roundcube filters) | |
| sieve_script personal { | |
| driver = file | |
| path = ~/sieve | |
| active_path = ~/.dovecot.sieve | |
| } | |
| # IMAPSieve for learning ham | |
| # https://gist.github.com/dmckeone/1b9cc69dba8d3435986dbe8b87070417 | |
| imapsieve_from Junk { | |
| sieve_script ham { | |
| type = after | |
| cause = copy | |
| path = /etc/dovecot/sieve/imapsieve/learn-ham.sieve | |
| } | |
| } | |
| # IMAPSieve for learning spam | |
| # https://gist.github.com/dmckeone/f88619ef61677267057fa5bcc6caf5cb | |
| mailbox Junk { | |
| sieve_script spam { | |
| type = before | |
| cause = copy | |
| path = /etc/dovecot/sieve/imapsieve/learn-spam.sieve | |
| } | |
| } | |
| # SSL Server settings | |
| # generated 2026-01-24, Mozilla Guideline v5.7, Dovecot 2.4.2, OpenSSL 3.4.0, modern config | |
| # https://ssl-config.mozilla.org/#server=dovecot&version=2.4.2&config=modern&openssl=3.4.0&guideline=5.7 | |
| ssl = required | |
| ssl_server_cert_file = /etc/ssl/{{ certificate_name }}/fullchain.pem | |
| ssl_server_key_file = /etc/ssl/{{ certificate_name }}/privkey.pem | |
| ssl_min_protocol = TLSv1.3 | |
| # https://doc.dovecot.org/2.4.2/core/summaries/settings.html#ssl_server_prefer_ciphers | |
| ssl_server_prefer_ciphers = server | |
| ssl_curve_list = X25519:prime256v1:secp384r1 | |
| # SSL Client settings | |
| # Allow use of self-signed Certificate Authorities (CA) | |
| ssl_client_ca_file = /etc/ssl/certs/ca-certificates.crt |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment