Setup Email Server From Scratch On FreeBSD #2 - 04 Dovecot IMAP
03 Postfix SMTPD <- Intro -> 05 PostfixAdmin
We believe in data independence, and support others who want data independence.
This tutorial is complete 2025-08-14 except there is no page for setting up postscreen.
This is version 2 and everthing works.
############################### # INSTALL DOVECOT IMAP SERVER # ###############################
If you have not install ports Update ports tree, ignore the Device busy, zfs needs to be unmounted to remove the actual directory, but we don't need to remove it.
rm -rf /usr/ports
rm: /usr/ports: Device busy
ls -la /usr/ports
drwxr-xr-x 2 root wheel 2 Aug 6 09:19 ./
drwxr-xr-x 15 root wheel 15 Jun 6 01:34 ../
pkg install portsnap
mkdir -p /var/db/portsnap
cd /usr/ports
portsnap fetch
portsnap extract
After upgrading ports and doing a pkg update pkg upgrade it replaced the compiled postfix dovecot with pkg. I had to reinstall postfix and dovecot from ports, if you go into dovecot-fts-flatcurve and to make rmconfig it doesn't remove the config in /ports/dovecot, so go to ports dovecot.
Additionally when doing pkg update pkg upgrade, if the pkg version of software is going to reinstall over the ports compiled version, use pkg lock pkgname to prevent just these packages from being replaced or upgrade. Then use ports to manage only those packages.
cd /usr/ports/mail/dovecot-fts-flatcurve
make rmconfig
make clean
cd /usr/ports/mail/dovecot-pigeonhole
make rmconfig
make clean
cd /usr/ports/mail/dovecot
make rmconfig
make clean
Then go back to flatcurve and pigeonhole and make and make install again
cd /usr/ports/mail/dovecot
../dovecot-fts-flatcurve
make
make install
Prevent pkg from overwriting compiled ports version of dovecot
pkg lock dovecot
For ports update use 'portsnap fetch update'.
If you plan to use ARGON2I scheme compile as follows.
Missing ARGONI
/usr/local/bin/doveadm pw -l
SHA1 SSHA512 SCRAM-SHA-256 BLF-CRYPT PLAIN HMAC-MD5 OTP SHA512 SHA DES-CRYPT
CRYPT SSHA MD5-CRYPT PLAIN-MD4 PLAIN-MD5 SCRAM-SHA-1 SHA512-CRYPT CLEAR
CLEARTEXT SSHA256 MD5 PBKDF2 SHA256 CRAM-MD5 PLAIN-TRUNC SHA256-CRYPT SMD5
DIGEST-MD5 LDAP-MD5
Clean the dovecot ports if previously compiled
cd /usr/ports/mail/dovecot-fts-flatcurve
make deinstall
make rmconfig
make clean
cd /usr/ports/mail/dovecot
make deinstall
make rmconfig
make clean
Compile dovecot-flatcurve
cd /usr/ports/mail/dovecot-fts-flatcurve
make
make install
Prevent pkg from overwriting compiled ports version of dovecot
pkg lock dovecot
Choose these options
X DOCS
X EXAMPLES
X LIBSODIUM - you will need this for ARGON2I (postfixadmin)
X LIBWRAP
X LUA
X LZ4
X CDB
X LDAP
X MYSQL
ICU - NO IT FAILED EVERY TIME
X SOLR
X TEXTCAT
X GSSAPI-NONE
There are a lot of dependencies and compiling will take some time. Several screens will popup with options to select, continue with default options already selected.
make install
libtool --finish /usr/local/lib/dovecot/doveadm
libtool --finish /usr/local/lib/dovecot
Check if user and groups were created, if not create them.
grep dovecot /etc/passwd /etc/group
/etc/passwd:dovecot:*:143:143:Dovecot User:/var/empty:/usr/sbin/nologin
/etc/group:dovecot:*:143:
Enable dovecot
sysrc dovecot_enable="YES"
To avoid a risk of mailbox corruption, do not set the security.bsd.see_other_uids or .see_other_gids sysctls to 0 if Dovecot is storing mail for multiple concurrent users (PR 218392).
Similarly, setting sysctls security.bsd.hardlink_check_uid or security.bsd.hardlink_check_gid to 1 might result in non-working mailboxes, depending on what mailbox locking mechanism is used (PR 242223).
If you want to be able to search within attachments using the decode2text plugin, you'll need to install textproc/catdoc, and one of graphics/xpdf or graphics/poppler-utils.
Both xpf and poppler-utils have security or dependency issues and fail to compile so use pkg.
pkg install catdoc
pkg install xpdf
pkg install poppler-utils
echo 'DEFAULT_VERSIONS+=ssl=openssl' >> /etc/make.conf
Generate the dh.pem for Dovecot
openssl dhparam -out /usr/local/etc/dovecot/dh.pem 4096
Copy example configuation files
cp -R /usr/local/etc/dovecot/example-config/* /usr/local/etc/dovecot
Configure Dovecot, Dovecot won't start without the certs and vmail configured.
Edit 10-ssl.conf
nano /usr/local/etc/dovecot/conf.d/10-ssl.conf
--- setup with default certs and multidomain selection ---
ssl = required
#ssl_cert = </etc/ssl/certs/dovecot.pem
#ssl_key = </etc/ssl/private/dovecot.pem
ssl_cert = </usr/local/etc/letsencrypt/live/mail.okbsd.com/fullchain.pem
ssl_key = </usr/local/etc/letsencrypt/live/mail.okbsd.com/privkey.pem
local_name mail.okbsd.com {
ssl_cert = </usr/local/etc/letsencrypt/live/mail.okbsd.com/fullchain.pem
ssl_key = </usr/local/etc/letsencrypt/live/mail.okbsd.com/privkey.pem
}
local_name imap.okbsd.com {
ssl_cert = </usr/local/etc/letsencrypt/live/mail.okbsd.com/fullchain.pem
ssl_key = </usr/local/etc/letsencrypt/live/mail.okbsd.com/privkey.pem
}
ssl_dh = </usr/local/etc/dovecot/dh.pem
ssl_min_protocol = TLSv1.2
ssl_prefer_server_ciphers = yes
Check if vmail user and group exist.
grep vmail /etc/passwd /etc/group
Add the vmail user and group
pw useradd -c "" -n vmail -s /usr/bin/nologin -d /nonexistent -u 2000
grep vmail /etc/passwd /etc/group
/etc/passwd:vmail:*:2000:2000::/nonexistent:/usr/bin/nologin
/etc/group:vmail:*:2000:
service dovecot start
service dovecot status
dovecot is running as pid 22413.
dovecot --version
2.3.21.1 (d492236fa0)
Check for LIBSODIUM/ARGON2I support if you plan to use it.
/usr/local/bin/doveadm pw -l
SHA1 SSHA512 SCRAM-SHA-256 BLF-CRYPT PLAIN HMAC-MD5 OTP SHA512 SHA DES-CRYPT
CRYPT SSHA MD5-CRYPT PLAIN-MD4 PLAIN-MD5 SCRAM-SHA-1 SHA512-CRYPT CLEAR
CLEARTEXT ARGON2I ARGON2ID SSHA256 MD5 PBKDF2 SHA256 CRAM-MD5 PLAIN-TRUNC
SHA256-CRYPT SMD5 DIGEST-MD5 LDAP-MD5
Test ARGON2I
/usr/local/bin/doveadm pw -s ARGON2I -r 5 -p hello
{ARGON2I}$argon2i$v=19$m=32768,t=5,p=1$RyC0o8I14UBuFpYHC61HEg$i7BQp0F4 ... etc.
openssl version
OpenSSL 3.0.16 11 Feb 2025 (Library: OpenSSL 3.0.16 11 Feb 2025)
Comment out the following in openssl, not compatible with dovecot lmtp
nano /etc/ssl/openssl.cnf
# providers = provider_sect
Add dovecot user to mail group so it can read the maildir
pw groupmod mail -m dovecot
grep dovecot /etc/group
mail:*:6:postfix,dovecot
dovecot:*:143:
################################ # Enable Submission in Postfix # ################################
I modified for FreeBSD but shamelessly copied parts from linuxbabe.com. He deserves a coffee or 100 coffee's! His tutorial is for Debian and a little out of date but still a fantastic guide!
Backup Postfix configs
cd /usr/local/etc
cp -R postfix postfix_backup
Backup Dovecot configs
cd /usr/local/etc
cp -R dovecot dovecot_backup
Paste the following into master.cf
nano /usr/local/etc/postfix/master.cf
submission inet n - y - - smtpd
-o syslog_name=postfix/submission
-o smtpd_tls_security_level=encrypt
-o smtpd_tls_wrappermode=no
-o smtpd_sasl_auth_enable=yes
-o smtpd_relay_restrictions=permit_sasl_authenticated,reject
-o smtpd_recipient_restrictions=permit_mynetworks,permit_sasl_authenticated,reject
-o smtpd_sasl_type=dovecot
-o smtpd_sasl_path=private/auth
smtps inet n - y - - smtpd
-o syslog_name=postfix/smtps
-o smtpd_tls_wrappermode=yes
-o smtpd_sasl_auth_enable=yes
-o smtpd_relay_restrictions=permit_sasl_authenticated,reject
-o smtpd_recipient_restrictions=permit_mynetworks,permit_sasl_authenticated,reject
-o smtpd_sasl_type=dovecot
-o smtpd_sasl_path=private/auth
If you want to compare what is configured between 2 machines useful commands
postconf | grep <value>
Modify or add the following values
nano /usr/local/etc/postfix/main.cf
myhostname = smtp.okbsd.com
mydomain = okbsd.com
myorigin = $mydomain
inet_interfaces = all
# mydestination = $myhostname, localhost.$mydomain, localhost
mydestination = $mydomain, $myhostname, localhost.$mydomain, localhost
local_recipient_maps = unix:passwd.byname $alias_maps
#mynetworks_style = host
mynetworks_style = ${{$compatibility_level} <level {2} ? {subnet} : {host}}
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128
smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination
relay_domains = ${{$compatibility_level} <level {2} ? {$mydestination} : {}}
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases
home_mailbox = Maildir/
mail_spool_directory = /var/mail
smtpd_banner = $myhostname ESMTP
setgid_group = maildrop
inet_protocols = all
smtp_tls_CApath = /etc/ssl/certs
shlib_directory = /usr/local/lib/postfix
meta_directory = /usr/loc/libexec/postfix
mailbox_size_limit = 0
message_size_limit = 52428800
# Enable TLS Encryption when Postfix receives incoming emails
smtpd_tls_cert_file=/usr/local/etc/letsencrypt/live/mail.okbsd.com/fullchain.pem
smtpd_tls_key_file=/usr/local/etc/letsencrypt/live/mail.okbsd.com/privkey.pem
smtpd_tls_security_level=may
smtpd_tls_loglevel = 1
smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
tls_server_sni_maps = hash:/usr/local/etc/postfix/sni_maps
# Enable TLS Encryption when Postfix sends outgoing emails
smtp_tls_security_level = may
smtp_tls_loglevel = 1
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
# Enforce TLSv1.3 or TLSv1.2
smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
smtpd_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
smtp_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
smtp_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
Create Postfix Server Name Indication SNI Maps
nano /usr/local/etc/postfix/sni_maps
mail.okbsd.com /usr/local/etc/letsencrypt/live/mail.okbsd.com/privkey.pem /usr/local/etc/letsencrypt/live/mail.okbsd.com/fullchain.pem
smtp.okbsd.com /usr/local/etc/letsencrypt/live/mail.okbsd.com/privkey.pem /usr/local/etc/letsencrypt/live/mail.okbsd.com/fullchain.pem
postmap -F /usr/local/etc/postfix/sni_maps
Restart Postfix
service postfix restart
postfix/postfix-script: stopping the Postfix mail system
postfix/postfix-script: starting the Postfix mail system
Check if postfix is running
service postfix status
postfix is running as pid 22606.
Finish Configuring Dovecot
To view Dovecot configuration
doveadm config
Edit dovecot.conf to enable lmtp
nano /usr/local/etc/dovecot/dovecot.conf
protocols = imap lmtp
To find the mail_spool_dir
postconf mail_spool_directory
mail_spool_directory = /var/mail
Edit 10-mail.conf - Use <control>-w to search for values to change
nano /usr/local/etc/dovecot/conf.d/10-mail.conf
mail_location = maildir:~/Maildir
# later mail_home = /var/vmail/%d/%n
mail_privileged_group = mail
Edit 10-master.conf
nano /usr/local/etc/dovecot/conf.d/10-master.conf
service lmtp {
unix_listener /var/spool/postfix/private/dovecot-lmtp {
mode = 0600
user = postfix
group = postfix
}
# Create inet listener only if you can't use the above UNIX socket
#inet_listener lmtp {
# Avoid making LMTP visible for the entire internet
#address =
#port =
#}
}
service auth {
unix_listener auth-userdb {
#mode = 0666
#user =
#group =
}
# Postfix smtp-auth
unix_listener /var/spool/postfix/private/auth {
mode = 0660
user = postfix
group = postfix
}
# Auth process is run as this user.
#user = $default_internal_user
}
Edit 20-lmtp.conf
nano /usr/local/etc/dovecot/conf.d/20-lmtp.conf
protocol lmtp {
# Space separated list of plugins to load (default is global mail_plugins).
mail_plugins = $mail_plugins
}
Edit 10-auth.conf
nano /usr/local/etc/dovecot/conf.d/10-auth.conf
disable_plaintext_auth = yes
auth_default_realm = okbsd.com
auth_username_format = %n
auth_mechanisms = plain login
!include auth-system.conf.ext
# !include auth-sql.conf.ext
auth_debug = yes
auth_debug_passwords = yes
Edit 15-mailboxes.conf
nano /usr/local/etc/dovecot/conf.d/15-mailboxes.conf
mailbox Drafts {
auto = subscribe
special_use = \Drafts
}
mailbox Junk {
auto = subscribe
special_use = \Junk
autoexpunge = 30d
}
mailbox Trash {
auto = subscribe
special_use = \Trash
autoexpunge = 30d
}
# For \Sent mailboxes there are two widely used names. We'll mark both of
# them as \Sent. User typically deletes one of them if duplicates are created.
mailbox Sent {
auto = subscribe
special_use = \Sent
}
mailbox "Sent Messages" {
special_use = \Sent
}
Modify /usr/local/etc/postfix/main.cf
nano /usr/local/etc/postfix/main.cf
--- add at end of file ---
mailbox_transport = lmtp:unix:private/dovecot-lmtp
smtputf8_enable = no
Restart and Postfix
service dovecot restart
service dovecot status
dovecot is running as pid 22680
service postfix restart
service postfix status
dovecot is running as pid 22791
Check if Dovecot is listening on ports
sockstat -l | grep dovecot | egrep -E "143|993"
root dovecot 22680 36 tcp4 *:143 *:*
root dovecot 22680 37 tcp6 *:143 *:*
root dovecot 22680 38 tcp4 *:993 *:*
root dovecot 22680 39 tcp6 *:993 *:*
############################### # Autoconfig and Autodiscover # ###############################
pkg install git
cd /tmp
git clone https://github.com/smartlyway/email-autoconfig-php
cd /tmp/email-autoconfig-php/mail
mkdir -p /usr/local/www/okbsd/mail
cp /tmp/email-autoconfig-php/mail/config-v1.1.xml /usr/local/www/okbsd/mail
Edit these files and change to suit your needs, this is tested and works with Thunderbird. Make sure change the SMTP settings to 587 with STARTTLS in config-v1.1.xml.
nano /usr/local/www/okbsd/mail/config-v1.1.xml
<?xml version="1.0"?>
<clientConfig version="1.1">
<emailProvider id="okbsd.com">
<domain>okbsd.com</domain>
<displayName>okbsd.com</displayName>
<displayShortName>okbsd.com</displayShortName>
<incomingServer type="imap">
<hostname>imap.okbsd.com</hostname>
<port>993</port>
<socketType>SSL</socketType>
<authentication>password-cleartext</authentication>
<username>%EMAILADDRESS%</username>
</incomingServer>
<outgoingServer type="smtp">
<hostname>smtp.okbsd.com</hostname>
<port>587</port>
<socketType>STARTTLS</socketType>
<username>%EMAILADDRESS%</username>
<authentication>password-cleartext</authentication>
</outgoingServer>
</emailProvider>
</clientConfig>
Configure Autodiscover for Outlook clients.
cp -r /tmp/email-autoconfig-php/Autodiscover/Autodiscover.xml /usr/local/www/okbsd/mail
nano /usr/local/www/okbsd/mail/Autodiscover.xml/index.php
<?php
$raw = file_get_contents('php://input');
$matches = array();
preg_match('/<EMailAddress>(.*)<\/EMailAddress>/', $raw, $matches);
header('Content-Type: application/xml');
?>
<Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/responseschema/2006">
<Response xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a">
<User>
<DisplayName>OK Biz Mail</DisplayName>
</User>
<Account>
<AccountType>email</AccountType>
<Action>settings</Action>
<Protocol>
<Type>IMAP</Type>
<Server>imap.okbsd.com</Server>
<Port>993</Port>
<DomainRequired>off</DomainRequired>
<SPA>off</SPA>
<SSL>on</SSL>
<AuthRequired>on</AuthRequired>
<LoginName><?php echo $matches[1]; ?></LoginName>
</Protocol>
<Protocol>
<Type>SMTP</Type>
<Server>smtp.okbsd.com</Server>
<Port>587</Port>
<DomainRequired>off</DomainRequired>
<SPA>off</SPA>
<SSL>on</SSL>
<AuthRequired>on</AuthRequired>
<LoginName><?php echo $matches[1]; ?></LoginName>
</Protocol>
</Account>
</Response>
</Autodiscover>
You will need to add a SV records for Autodiscover for NameCheap I added ...
Namecheap Domain List -> DNS -> Advanced DNS
Type Service Protocol Priority Weight Port Target
SRV Record _autodiscover _tcp 5 0 443 mail.okbsd.com
Autoconfig needs the path http://autoconfig.okbsd.com/mail/config-v1.1.xml
nano /usr/local/etc/apache24/Includes/mail.okbsd.conf
ServerAlias autoconfig.okbsd.com
ServerAlias autodiscover.okbsd.com
# autoconfig
Alias /mail "/var/www/okbsd/mail"
<Directory /usr/local/www/okbsd/mail/>
Options FollowSymLinks
AllowOverride All
Require all granted
</Directory>
Autodiscover uses the host the SRV record points to which is mail.okbsd.com and it already has a cert. Autodiscover needs path https://okbsd.com/Autodiscover/Autodiscover.xml.
nano /usr/local/etc/apache24/Includes/ssl-mail.okbsd.conf
<IfModule mod_ssl.c>
SSLStaplingCache shmcb:/var/run/apache2/stapling_cache(128000)
<VirtualHost *:443>
ServerName mail.okbsd.com
#ServerAlias smtp.okbsd.com
#ServerAlias imap.okbsd.com
#ServerAlias autoconfig.okbsd.com
#ServerAlias autodiscover.okbsd.com
ServerAdmin postmaster@okbsd.com
DirectoryIndex index.php index.html
DocumentRoot /usr/local/www/okbsd/html/
<Directory /usr/local/www/okbsd/html/>
Options FollowSymLinks
AllowOverride All
Require all granted
</Directory>
Alias /mail "/usr/local/www/okbsd/mail/"
Alias "/Autodiscover/Autodiscover.xml" "/usr/local/www/okbsd/mail/Autodiscover.xml/index.php"
<Directory /usr/local/www/okbsd/mail/>
DirectorySlash Off
# Options FollowSymLinks
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
</Directory>
Include /usr/local/etc/letsencrypt/options-ssl-apache.conf
Header always set Strict-Transport-Security "max-age=31536000"
SSLUseStapling on
SSLCertificateFile /usr/local/etc/letsencrypt/live/mail.okbsd.com/fullchain.pem
SSLCertificateKeyFile /usr/local/etc/letsencrypt/live/mail.okbsd.com/privkey.pem
</VirtualHost>
</IfModule>
apachectl restart
Confirm these files can be accessed by http and https with a web browser and that they output xml.
http://mail.okbsd.com/mail/config-v1.1.xml
https://mail.okbsd.com/Autodiscover/Autodiscover.xml
########################################################## # Setup Thunderbird email client - use advanced settings # ##########################################################
If you don't have thunderbird search download thunderbird mail with your web browser and install it.
Settings -> Account Settings -> Account Actions -> Add Mail Account
Incoming / IMAP
Full Name: Jack Pumpkin
Email address: jack@okbsd.com
Password: <secretpassword>
Click Configure Manually
Protocol: IMAP
Hostname: imap.okbsd.com
Port: 993
Security: SSL/TLS
Outgoing / SMTP
Username: jack@okbsd.com
Protocol: Outgoing
Hostname: smtp.okbsd.com
Port: 587
Security: STARTTLS
Username: jack@okbsd.com
<Re-test>
###################### # Thunderbird Errors # ######################
If you encounter the following Thunderbird error when sending mail change SSL/TLS to STARTTLS for the Outgoing Server.
Sending of the message failed. The message could not be sent because the connection to Outgoing server (SMTP) mx.coragarden.com was lost in the middle of the transaction. Try again.
This seems to occur when Thunderbird caches bad autoconfig data from the server and doesn't set the encryption method properly.
Go to Account Settings / Outgoing Server and delete the relevant smtp server entries. Then delete the account(s). Then go to Tools / Clear Recent History / Delete Everything. Restart Thunderbird. Run a fresh test.
I tried SNI, server name indication wasn't working.
Sending of the message failed. An error occurred while sending mail: Outgoing server (SMTP) error. The server responded: TLS not available due to local problem.
SNI configuration requires default certs in both Postfix and Dovecot. After SNI was fixed it was necessary to stop and restart Thunderbird to test if it was fixed.
At first I could not authenticate because I used auth_username_format = %u. At this stage in the setup use auth_username_format = %n, and switch to %u later on.
To troubleshoot use check /var/log/maillog
cat /var/log/maillog
If you can't find the error try clearing the log and tail it from one terminal while sending and recieving mail with the mail client.
clear the log
echo '' > /var/log/maillog
tail it so you can watch the output
tail -f -300 /var/log/maillog
################### # Troubleshooting # ###################
Caution! Thunderbird caches data! When testing, delete the relevant outgoing server(s), delete the relevant mail account(s) in Thunderbired, go to Tools -> Clear Recent History and delete everything, close and restart Thunderbird.
Then add the account(s) back as new to test again.
Test autoconfig by removing the account in Thunderbird and adding it back. If it is working properly it should configure quickly with imap.okbsd.com. If Thunderbird said it is testing well known server names, takes a while, and comes up with mail.okdeb.com then autoconfig is not working properly and check your apache configuration and paths. See the readme in /tmp/email-autoconfig-php.
If you get the following error in /var/log/maillog, you forgot postmap -F /usr/local/etc/postfix/sni_maps or the path in /usr/local/etc/postfix/main.cf is wrong.
warning: table hash:/usr/local/etc/postfix/sni_maps.db: key smtp.okbsd.com: malformed BASE64 value:
##################################### # Next we will install PostfixAdmin # #####################################