Skip to content

Instantly share code, notes, and snippets.

@dmckeone
Last active January 26, 2026 21:35
Show Gist options
  • Select an option

  • Save dmckeone/7cdf170624d77c4ddcdce1721c6f760e to your computer and use it in GitHub Desktop.

Select an option

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)
# 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