Setup Email Server From Scratch On FreeBSD #2 - 07 RoundCube WebMail
06 SPF DMARC And DKIM <- Intro -> 09 Create Virtual Domains
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.
###################################### # Setup RoundCube Webmail On FreeBSD # ######################################
Getting Roundcube setup and installing the modules could be a tutorial by itself.
Download Roundcube from https://roundcube.net/download/ and get the complete stable version roundcubemail-1.6.11-complete.tar.gz.
cd /tmp
wget https://github.com/roundcube/roundcubemail/releases/download/1.6.11/roundcubemail-1.6.11-complete.tar.gz
tar xfzv roundcubemail-1.6.11-complete.tar.gz
mv /tmp/roundcubemail-1.6.11 /usr/local/www/roundcube
chown -R root:wheel /usr/local/www/roundcube
mkdir -p /usr/local/www/roundcube/temp
mkdir -p /usr/local/www/roundcube/logs
chown -R www:www /usr/local/www/roundcube/temp
chown -R www:www /usr/local/www/roundcube/logs
Add PHP Modules - some of these may not be neeeded, but with these everything works as expected
pkg install php83-pear-DB_ldap2 php83-pecl-imagick php83-pecl-redis php83-mysqli openssl zoneinfo-2025.a
pkg install php83-imap php83-curl php83-zip php83-mbstring php83-bz2 php83-intl php83-gmp php83-gd
pkg install php83-pear wget Crypt_GPG py311-lesscpy php83-composer gnupg
pkg install php83-gettext php83-phar php83-enchant sabredav sabre php83-xmlreader
pkg install gnupg
pear install Crypt_GPG
We need lessc and lesscpy is not lessc, so p311-lesscpy may not be needed.
# pkg install py311-lesscpy
Add /proc filesystem - some python functions require /proc.
mount -t procfs proc /proc
echo 'proc /proc procfs rw 0 0' >> /etc/fstab
Install less package with npm which has lessc.
pkg install node
pkg install npm
npm install -g less
Get your timezone for adding to php.ini
ls /usr/share/zoneinfo/*
Configure php.ini including the timezone - the values in php.ini affect roundcube max attachment size.
nano /usr/local/etc/php.ini
max_execution_time = 600
max_input_time = 300
max_input_vars = 3000
memory_limit = 256M
post_max_size = 200M
upload_max_filesize = 100M
...
date.timezone = US/Pacific
Edit MySQL configuration
nano /usr/local/etc/mysql/my.cnf
--- add to the bottom of the mysqld section ---
[mysqld]
...
max_connections = 500
tmp_table_size = 128M
max_heap_table_size = 128M
sort_buffer_size = 2M
slow_query_log = 1
# skip-name-resolve
lower_case_table_names=1
character-set-server = utf8mb4
# collation-server = utf8mb4_general_ci
collation_server = utf8mb4_unicode_520_ci
service mysql-server restart
Create Roundcube Database
mysql -u root
> create database roundcubemail;
> CREATE USER 'roundcube'@'localhost' IDENTIFIED WITH caching_sha2_password BY 'secretpasswd';
> grant all privileges on roundcubemail.* to 'roundcube'@'localhost';
> flush privileges;
> exit;
Test the mysql login
mysql -u roundcube -psecretpasswd roundcubemail
> exit;
Import Database Tables
mysql roundcubemail < /usr/local/www/roundcube/SQL/mysql.initial.sql
Change Apache DocumentRoot and Directory to Roundcube
nano /usr/local/etc/apache24/Includes/ssl-mail.okbsd.conf
#DocumentRoot /usr/local/www/okbsd/html/
#<Directory /usr/local/www/okbsd/html/>
# Options FollowSymLinks
# AllowOverride All
# Require all granted
#</Directory>
DocumentRoot /usr/local/www/roundcube/
Alias /webmail "/usr/local/www/roundcube"
Alias /roundcube "/usr/local/www/roundcube"
<Directory /usr/local/www/roundcube/>
# Options FollowSymLinks MultiViews
Options FollowSymLinks
AllowOverride All
# Require all granted
Require all denied
Require ip 10.0.0.0/8 192.168.0.0/16 127.0.0.1 trusted_ip3 trusted_ipv6
</Directory>
Don't use Alias /mail "/usr/share/roundcube" because /mail is used for autoconfig. MultiViews doesn't seem to be required. It is better to configure Roundcube with ssl/https only to force https password encryption.
service php_fpm restart
apachectl restart
Configure Roundcube imap, smtp, and spell checking.
cd /usr/local/www/roundcube/config
cp /usr/local/www/roundcube/config/config.inc.php.sample /usr/local/www/roundcube/config/config.inc.php
Please change the DES Key to a random 24 char string and make it exactly 24 chars and set your super_secret_password the same as you provided when creating the roundcube database. Please count the chars in your key, base 64 defaults to 32 so remove 8 chars.
openssl rand -base64 24
nano /usr/local/www/roundcube/config/config.inc.php
$config['db_dsnw'] = 'mysql://roundcube:secretpasswd@localhost/roundcubemail';
$config['imap_host'] = 'tls://imap.okbsd.com:143';
$config['smtp_host'] = 'tls://smtp.okbsd.com:587';
// Replace the default key with 24 random characters.
$config['des_key'] = '123456789012345678901234';
// Enable more plugins
$config['plugins'] = ['acl', 'additional_message_headers', 'archive', 'attachment_reminder',
'autologon', 'debug_logger', 'emoticons', 'enigma', 'filesystem_attachments', 'help',
'hide_blockquote', 'http_authentication', 'identicon', 'identity_select', 'jqueryui',
'krb_authentication', 'managesieve', 'markasjunk', 'new_user_dialog', 'new_user_identity',
'newmail_notifier', 'password', 'reconnect', 'redundant_attachments', 'show_additional_headers',
'squirrelmail_usercopy', 'subscriptions_option', 'userinfo', 'vcard_attachments', 'virtuser_file',
'virtuser_query', 'zipdownload','automatic_addressbook'];
// skin name: folder from skins/
$config['enable_spellcheck'] = true;
$config['spellcheck_engine'] = 'enchant';
// this will make max upload 75% so 100M => 75M
$config['max_message_size'] = '134M';
$config['create_default_folders'] = true;
?>
Fix directories and permissions - any files can contain mysql passwords should not writable by user or group www and not be world readable.
chown root:www /usr/local/www/roundcube/config/config.inc.php
chmod 640 /usr/local/www/roundcube/config/config.inc.php
Enable Enigma - needed for setting mail users signature and pgp options.
Create enigma home
mkdir -p /usr/local/www/roundcube/plugins/enigma/home
chown www:www /usr/local/www/roundcube/plugins/enigma/home
chmod 750 /usr/local/www/roundcube/plugins/enigma/home
Configure Enigma
cd /usr/local/www/roundcube/plugins/enigma
cp /usr/local/www/roundcube/plugins/enigma/config.inc.php.dist /usr/local/www/roundcube/plugins/enigma/config.inc.php
nano /usr/local/www/roundcube/plugins/enigma/config.inc.php
$config['enigma_pgp_homedir'] = "/usr/local/www/roundcube/plugins/enigma/home";
chown root:www /usr/local/www/roundcube/plugins/enigma/config.inc.php
chmod 640 /usr/local/www/roundcube/plugins/enigma/config.inc.php
Automatic Addressbook
cd /tmp
git clone https://github.com/sblaisot/automatic_addressbook.git
mv /tmp/automatic_addressbook /usr/local/www/roundcube/plugins
cd /usr/local/www/roundcube/plugins/automatic_addressbook/SQL
mysql -u roundcube -psecretpasswd roundcubemail < mysql.initial.sql
cd /usr/local/www/roundcube/plugins/automatic_addressbook/config
cp /usr/local/www/roundcube/plugins/automatic_addressbook/config/config.inc.php.dist /usr/local/www/roundcube/plugins/automatic_addressbook/config/config.inc.php
chown root:www /usr/local/www/roundcube/plugins/automatic_addressbook/config/config.inc.php
chmod 640 /usr/local/www/roundcube/plugins/automatic_addressbook/config/config.inc.php
Test Roundcube Login ...
https://mail.okbsd.com
Check Settings -> Identities -> Click On User -> If fixed it shows Signature and Manage PGP Keys
Sieve Install - seive is provided by dovecot-pideonhole and is needed for mail filtering in Roundcube.
echo 'DEFAULT_VERSIONS+=ssl=openssl' >> /etc/make.conf
cd /usr/ports/mail/dovecot-pigeonhole
make
X DOCS
X EXAMPLES
X LDAP
X MANAGESIEVE
X GSSAPI_NONE
make install
Prevent pkg from overwriting compiled ports version of dovecot
pkg lock dovecot
nano /usr/local/etc/dovecot/dovecot.conf
protocols = imap lmtp sieve
# ... append to bottom
service managesieve-login {
inet_listener sieve {
port = 4190
}
inet_listener sieve_deprecated {
port = 2000
}
service_count = 1
process_min_avail = 0
vsz_limit = 64M
}
service managesieve {
process_limit = 1024
}
protocol sieve {
managesieve_max_line_length = 65536
managesieve_implementation_string = dovecot
log_path = /var/log/dovecot-sieve-errors.log
info_log_path = /var/log/dovecot-sieve.log
}
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
}
}
nano /usr/local/etc/postfix/main.cf
mailbox_transport = lmtp:unix:private/dovecot-lmtp
smtputf8_enable = no
nano /usr/local/etc/dovecot/conf.d/15-lda.conf
protocol lda {
# Space separated list of plugins to load (default is global mail_plugins).
mail_plugins = $mail_plugins sieve
}
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 quota sieve
}
nano /usr/local/etc/dovecot/conf.d/10-mail.conf
mail_location = maildir:~/Maildir
mail_home = /var/vmail/%d/%n
mkdir -p /usr/local/etc/dovecot/sieve/global
nano /usr/local/etc/dovecot/sieve/before-global.sieve
require "fileinto";
if header :contains "X-Spam-Flag" "YES"
{
fileinto "Junk";
stop;
}
sievec /usr/local/etc/dovecot/sieve/before-global.sieve
nano /usr/local/etc/dovecot/conf.d/90-plugin.conf
plugin {
#setting_name = value
sieve = file:~/sieve;active=~/.dovecot.sieve
sieve_default = /usr/local/etc/dovecot/sieve/default.sieve
sieve_global = /usr/local/etc/dovecot/sieve/global/
sieve_before = /usr/local/etc/dovecot/sieve/before-global.sieve
}
service postfix restart
service dovecot restart
Configure managesieve in Roundcube
cd /usr/local/www/roundcube/plugins/managesieve
cp /usr/local/www/roundcube/plugins/managesieve/config.inc.php.dist /usr/local/www/roundcube/plugins/managesieve/config.inc.php
chown root:www /usr/local/www/roundcube/plugins/managesieve/config.inc.php
chmod 640 /usr/local/www/roundcube/plugins/managesieve/config.inc.php
nano /usr/local/www/roundcube/plugins/managesieve/config.inc.php
--- add or change ---
// $config['managesieve_host'] = 'localhost';
$config['managesieve_host'] = 'tls://smtp.okbsd.com';
$config['managesieve_usetls'] = true;
$config['managesieve_default'] = '/usr/local/etc/dovecot/sieve/global';
$config['managesieve_debug'] = true;
$config['managesieve_usetls'] = true;
apachectl restart
Remove Roundcube Header
nano /usr/local/etc/postfix/smtp_header_checks
--- create or add ---
/^User-Agent.*Roundcube Webmail/ IGNORE
nano /usr/local/etc/postfix/main.cf
--- append to end of file ---
smtp_header_checks = regexp:/usr/local/etc/postfix/smtp_header_checks
postmap /usr/local/etc/postfix/smtp_header_checks
service postfix restart
Configure RoundCube Password Plugin
cd /usr/local/www/roundcube/plugins/password
cp /usr/local/www/roundcube/plugins/password/config.inc.php.dist /usr/local/www/roundcube/plugins/password/config.inc.php
nano /usr/local/www/roundcube/plugins/password/config.inc.php
$config['password_algorithm'] = 'dovecot';
$config['password_dovecotpw'] = '/usr/local/bin/doveadm pw -r 5';
$config['password_dovecotpw_method'] = 'ARGON2I';
$config['password_dovecotpw_with_method'] = true;
$config['password_db_dsn'] = 'mysql://postfixadmin:secretpasswd@localhost/postfixadmin';
$config['password_query'] = 'UPDATE mailbox SET password=%P,modified=NOW() WHERE username=%u';
$config['password_strength_driver'] = null;
$config['password_minimum_length'] = 8;
$config['password_minimum_score'] = 0;
chown root:www /usr/local/www/roundcube/plugins/password/config.inc.php
chmod 640 /usr/local/www/roundcube/plugins/password/config.inc.php
Enable plugins 'automatic_addressbook','password' in Roundcube remove redundant_attachments
nano /usr/local/www/roundcube/config/config.inc.php
$config['plugins'] = ['acl', 'additional_message_headers', 'archive', 'attachment_reminder',
'autologon', 'debug_logger', 'emoticons', 'enigma', 'filesystem_attachments', 'help',
'hide_blockquote', 'http_authentication', 'identicon', 'identity_select', 'jqueryui',
'krb_authentication', 'managesieve', 'markasjunk', 'new_user_dialog', 'new_user_identity',
'newmail_notifier', 'password', 'reconnect', 'show_additional_headers',
'squirrelmail_usercopy', 'subscriptions_option', 'userinfo', 'vcard_attachments', 'virtuser_file',
'virtuser_query', 'zipdownload','automatic_addressbook','password'];
//'redundant_attachments',
If you have problems double check ARGON2I has been compiled into dovecot.
/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
Check that changing password works in roundcube
I was not able to change passwords because the password_db_dsn default is to use 127.0.0.1 and I had put skip-name-resolve in my.cnf.
cat /usr/local/www/roundcube/logs/errors.log
Host '127.0.0.1' is not allowed to connect to this MySQL server
mysql -u postfixadmin -psecretpasswd -h 127.0.0.1 postfixadmin
Host '127.0.0.1' is not allowed to connect to this MySQL server
mysql -u postfixadmin -psecretpasswd -h localhost postfixadmin
> exit
Two solutions, get rid of skip-name-resolve in my.cnf (preferred) or change the password_db_dsn line.
nano /usr/local/etc/mysql/my.cnf
# skip-name-resolve
nano /usr/local/www/roundcube/plugins/password/config.inc.php
// $config['password_db_dsn'] = 'mysql://postfixadmin:secretpasswd@127.0.0.1/postfixadmin';
$config['password_db_dsn'] = 'mysql://postfixadmin:secretpasswd@localhost/postfixadmin';
It is best to disable skip-name-resolve for flexibility but some sources say localhost uses the mysql.sock which is more efficient and secure than using the inet port 3306. When at all possible configure to unix sockets which have less overhead and are slightly faster.
Install Calendar
cd /tmp
git clone https://git.kolab.org/diffusion/RPK/roundcubemail-plugins-kolab.git
cd /usr/local/www/roundcube/plugins
cp -r /tmp/roundcubemail-plugins-kolab/plugins/calendar .
cp -r /tmp/roundcubemail-plugins-kolab/plugins/libcalendaring .
cp -r /tmp/roundcubemail-plugins-kolab/plugins/libkolab .
Install sabre/dav
cd /usr/local/www/roundcube
nano /usr/local/www/roundcube/composer.json
{
"name": "roundcube/roundcubemail",
"version": "1.6.11",
"description": "The Roundcube Webmail suite",
"license": "GPL-3.0-or-later",
"repositories": [
{
"type": "composer",
"url": "https://plugins.roundcube.net"
}
],
"require": {
"php": ">=7.3.0",
"pear/pear-core-minimal": "~1.10.1",
"pear/auth_sasl": "~1.1.0",
"pear/mail_mime": "~1.10.0",
"pear/net_smtp": "~1.10.0",
"pear/crypt_gpg": "~1.6.3",
"pear/net_sieve": "~1.4.5",
"roundcube/plugin-installer": "~0.3.1",
"roundcube/rtf-html-php": "~2.1",
"masterminds/html5": "~2.7.0",
"bacon/bacon-qr-code": "^2.0.0",
"guzzlehttp/guzzle": "^7.3.0",
"kolab/net_ldap3": "~1.1.4",
},
"suggest": {
"bjeavons/zxcvbn-php": "^1.0 required for Zxcvbn password strength driver"
},
"config": {
"allow-plugins": {
"roundcube/plugin-installer": true
}
}
}
Run composer to install the dependencies. Initially I just ran composer require sabre/dav but composer said it could not find the version of roundcube/roundcube so defaulted to 1.0.0 which downgraded the install. Adding "version": "1.6.11", is the above file fixed this and it would be better to do this first.
composer require sabre/dav ~4.7.0 sabre/vobject ^4.5.7 --with-all-dependencies
Test what versions were installed.
grep sabre composer.json
"sabre/dav": "~4.7.0",
"sabre/vobject": "^4.5.7"
Copy calendar configuration and set permissions.
cd /usr/local/www/roundcube/plugins/calendar
cp /usr/local/www/roundcube/plugins/calendar/config.inc.php.dist /usr/local/www/roundcube/plugins/calendar/config.inc.php
chown root:www /usr/local/www/roundcube/plugins/calendar/config.inc.php
chmod 640 /usr/local/www/roundcube/plugins/calendar/config.inc.php
Initialize the calendar database
cd /usr/local/www/roundcube/plugins/libkolab/SQL
mysql -u roundcube -psuper_secret_password roundcubemail < /usr/local/www/roundcube/plugins/libkolab/SQL/mysql.initial.sql
cd /usr/local/www/roundcube
bin/initdb.sh --dir=plugins/calendar/drivers/database/SQL
Generate the compiled css files with lessc, this will generate a message of missing svg files, but calendar works.
lessc -x /usr/local/www/roundcube/plugins/libkolab/skins/elastic/libkolab.less > /usr/local/www/roundcube/plugins/libkolab/skins/elastic/libkolab.min.css
Enable calendar in Roundcube main configuration file.
To customize displayed product_name based on the virtual server used include the routine shown here. Creating default folders is a nice touch. If you do not use the default path in apache set url_base in the roundcube configuration. Roundcube max "attachments" size is 2M by default, use max_message_size to override. Also, default values in php.ini need to be modified to increase max attachment size.
nano /usr/local/www/roundcube/config/config.inc.php
...
$config['db_dsnw'] = 'mysql://roundcube:secretpasswd@localhost/roundcubemail';
$config['imap_host'] = 'tls://imap.okbsd.com:143';
$config['smtp_host'] = 'tls://smtp.okbsd.com:587';
$config['smtp_user'] = '%u';
$config['smtp_pass'] = '%p';
$config['support_url'] = '';
...
// Name your service. This is displayed on the login screen and in the window title
$sn = $_SERVER['SERVER_NAME'];
if (preg_match('/coragarden\.com/', $sn)) {
$config['product_name'] = 'Cora Garden Webmail';
} else if (preg_match('/okbsd\.com/', $sn)) {
$config['product_name'] = 'OkBSD Webmail';
} else if (preg_match('/okdeb\.com/', $sn)) {
$config['product_name'] = 'OkDeb Webmail';
} else {
$config['product_name'] = 'Roundcube Webmail';
}
$config['des_key'] = '123456789012345678901234';
$config['plugins'] = ['acl', 'additional_message_headers', 'archive', 'attachment_reminder',
'autologon', 'debug_logger', 'emoticons', 'enigma', 'filesystem_attachments', 'help',
'hide_blockquote', 'http_authentication', 'identicon', 'identity_select', 'jqueryui',
'krb_authentication', 'managesieve', 'markasjunk', 'new_user_dialog', 'new_user_identity',
'newmail_notifier', 'password', 'reconnect', 'show_additional_headers',
'squirrelmail_usercopy', 'subscriptions_option', 'userinfo', 'vcard_attachments', 'virtuser_file',
'virtuser_query', 'zipdownload', 'automatic_addressbook', 'calendar', 'password'];
// 'redundant_attachments'
// skin name: folder from skins/
$config['skin'] = 'elastic';
$config['max_pagesize'] = 1000;
$config['enable_spellcheck'] = true;
$config['spellcheck_engine'] = 'enchant';
// this will make max upload 75% so 100M => 75M or 134M => 100M
$config['max_message_size'] = '134M';
$config['create_default_folders'] = true;
// $config['url_base'] = '/mail-login/';
?>
Test that everything works as expected
Install the larry skin which are compatable with calendar.
cd /tmp
git clone https://github.com/roundcube/larry.git
cp -r larry /var/www/roundcube/skins/
cd /var/www/roundcube/config
You can now select which skin you'd like.
Settings -> Preferences - > User Interface
There are many more roundcube skins but most require payment.
Free colorful larry skins https://github.com/roundcube/larry.git
DOES NOT WORK WITH CALENDAR!
cd /tmp
git clone https://github.com/texxasrulez/roundcube_skins.git
cd /tmp/roundcube_skins/skins
mv * /var/www/roundcube/skins
Try html5_notifier, I didn't see any notifications when I used with Firefox.
cd /tmp
git clone https://github.com/stremlau/html5_notifier.git
mv /tmp/html5_notifier /var/www/roundcube/plugins/
cd /var/www/roundcube/plugins/html5_notifier/config
cp /var/www/roundcube/plugins/html5_notifier/config/config.inc.php.dist \
/var/www/roundcube/plugins/html5_notifier/config/config.inc.php
nano /var/www/roundcube/config/config.inc.php
--- append to the end of modules list ---
'virtuser_query', 'zipdownload', 'automatic_addressbook', 'calendar','password', 'html5_notifier'];
A vexiadmin plugin allows users to manage spam filtering
Roundcube has an extensive set of plugins which are beyond the scope of this setup tutorial. It would be interesting to set this up with caldav cardav and maybe webdav could be integrated for sharing larger files.
########################################## # Next - Multiple "Virtual" Mail Domains # ##########################################