Setup Email Server From Scratch On FreeBSD - 08 Create Virtual Domains
07 RoundCube WebMail <- Intro -> 09 Blocking Spam
This tutorial is partially complete 2025-05-14
Postfix, Dovecot, & PostfixAdmin work with MySQL and virtual accounts
can be created with PostfixAdmin and used with an email client. SPF, OpenDKIM,
and DMARC milters, Roundcube with sieve filters and password plugins all work.
Roundcube kolab calendar works (2025-05-20)
################################
# Howto Create Virtual Domains #
################################
This page of the "FreeBSD Email From Scratch" tutorial assumes successful setup of the following ...
1. FreeBSD Server
2. FAMP - FreeBSD Apache MySQL/MariaDB PHP
3. Postfix - smtp server
4. Dovecot - imap server
5. PostfixAdmin - Postfix Web Administration
6. SPF DMARC and DKIM - email authetication and spam filters
7. Roundcube - Webmail
Setup DNS for New Domain(s)
A mail 147.135.65.97
AAAA mail 2604:2dc0:100:1261::10
A mx 147.135.65.97
AAAA mx 2604:2dc0:100:1261::10
CNAME autoconfig mx.okbsd.com
CNAME autodiscover mx.okbsd.com
CNAME www coragarden.com
TXT @ v=spf1 ip4:147.135.65.97 ip6:2604:2dc0:100:1261::10/64 mx ~all
TXT _dmarc v=DMARC1; p=quarantine; rua=mailto:postmaster@okbsd.com; ruf=mailto:postmaster@okbsd.com; sp=quarantine
SRV Record _autodiscover _tcp 5 0 443 mx.okbsd.com
# In Custom MX Records
MX @ mx.okbsd.com 0
MX @ mail.okbsd.com 10
# We use mx.okbsd.com and mail.okbsd.com as the MX host so we don't need to create A and AAAA records and certs for
# additional mx servers like mx.coragarden.com and mail.coragarden.com, but this example includes the A and AAAA
# records. Also DMARC reports are sent to the admin@okbsd.com email address. This means okbsd.com needs a DNS TXT entry
# to allow coragarden.com to send dmarc reports to okbsd.com. There is some benefit to adding A and AAAA records
# if users will access IMAP or webmail at mail.coragarden.com for example.
# DNS TXT Entry for okbsd.com
TXT coragarden.com._report._dmarc v=DMARC1;
# Setup DKIM Records For New Domain
mkdir /usr/local/etc/mail/keys/coragarden.com
# In this example we create a random selector but it is more important that the selector be unique and contain information
# to identify department date and key length.
Example: Like 2025-coragarden-sales-2024b or 2025-coragarden
# Generate a random DKIM selector -
openssl rand -base64 23
23 char Cy9jmkzC2USPfcJ4JIuzA07rpB0gSbA=
20 char Cy9jmkzC2USPfcJ4JIuzA07rpB0gS
opendkim-genkey -b 2048 -d coragarden.com -D /usr/local/etc/mail/keys/coragarden.com -s Cy9jmkzC2USPfcJ4JIuzA07rpB0gS -v
# new example without the subdirectory - modify path as needed below
opendkim-genkey -b 2048 -d coragarden.com -D /usr/local/etc/mail/keys -s 2025-coragarden -v
chown -R opendkim:opendkim /usr/local/etc/mail/keys
find /usr/local/etc/mail/keys -type d -exec chmod 500 {} \;
find /usr/local/etc/mail/keys -type f -exec chmod 400 {} \;
cat /usr/local/etc/mail/keys/coragarden.com/*.txt
# new example - modify your key path as needed since we removed a directory level
cat /usr/local/etc/mail/keys/2025-coragarden.txt
# Copy the keys to your name server record and remember to remove the extrs quotes and spaces in 2 sections of the DKIM line.
# Add it to the keytable file.
cat /usr/local/etc/mail/keys/coragarden.com/*.txt >> /usr/local/etc/mail/keytable
nano /usr/local/etc/mail/keytable
# ---
CRHdAnOqqitUaWRuNkHLdIpbgw76._domainkey.okbsd.com okbsd.com:CRHdAnOqqitUaWRuNkHLdIpbgw76:/usr/local/etc/mail/keys/okbsd.com/CRHdAnOqqitUaWRuNkHLdIpbgw76.private
Cy9jmkzC2USPfcJ4JIuzA07rpB0gS._domainkey.coragarden.com coragarden.com:Cy9jmkzC2USPfcJ4JIuzA07rpB0gS:/usr/local/etc/mail/keys/coragarden.com/Cy9jmkzC2USPfcJ4JIuzA07rpB0gS.private
# ---
# new example
2025-coragarden._domainkey.coragarden.com coragarden.com:2025-coragarden:/usr/local/etc/mail/keys/2025-coragarden.private
nano /usr/local/etc/mail/signingtable
# ---
*@okbsd.com CRHdAnOqqitUaWRuNkHLdIpbgw76._domainkey.okbsd.com
*@coragarden.com Cy9jmkzC2USPfcJ4JIuzA07rpB0gS._domainkey.coragarden.com
# ---
# new example
*@coragarden.com 2025-coragarden._domainkey.coragarden.com
service milter-opendkim restart
# By the time this is created the DNS has hopefully propagated and can be tested, test the key
opendkim-testkey -d coragarden.com -s Cy9jmkzC2USPfcJ4JIuzA07rpB0gS -vvv -x /usr/local/etc/mail/opendkim.conf
opendkim-testkey: checking key 'Cy9jmkzC2USPfcJ4JIuzA07rpB0gS._domainkey.coragarden.com'
opendkim-testkey: key secure
opendkim-testkey: key OK
# new example
opendkim-testkey -d coragarden.com -s 2025-coragarden -vvv -x /usr/local/etc/mail/opendkim.conf
# Setup Domain in PostfixAdmin
Domain List -> New Domain
Domain: coragarden.com
Description: gardening site
Pass expires : 3650
Add Domain
Virtual List - Add Mailbox
vuser @coragarden.com
Add User
Virtual List - redirect the default email addresses to a valid account (postmaster@domain.tld is recommended)
I changed all of mine to point to admin@okbsd.com
# While you could add another domain to your webserver it is easier to just login with roundcube at the okbsd.com
# address. If you want to use roundcube with new domain name setup apache and letsencrypt certs for the new domain.
# You could also put multiple domains in one cert and use ServerAlias in the same virtual host configuration.
# See step 2 if you want to add more certs to the same virtual host. At this point I decided to separate the mx mail
# to a separate directory from the gereral website directory.
mkdir -p /var/www/mx.okbsd/html
# move the autodiscover autoconfig
cd /var/www/okbsd/html
mv /var/www/okbsd/html/mail /var/www/mx.okbsd/html
nano /usr/local/etc/apache24/Includes/10-mx.okbsd.conf
# ---
...
ServerAlias mx.coragarden.com
ServerAlias mail.coragarden.com
ServerAlias autoconfig.coragarden.com
ServerAlias autodiscover.coragarden.com
ServerAdmin postmaster@okbsd.com
DocumentRoot /var/www/mx.okbsd/html
<Directory /var/www/mx.okbsd/html/>
...
# autoconfig
Alias /mail "/var/www/mx.okbsd/html/mail"
# ---
nano /usr/local/etc/apache24/Includes/10-mx.okbsd-le-ssl.conf
# ---
...
ServerAlias mx.coragarden.com
ServerAlias mail.coragarden.com
ServerAlias autoconfig.coragarden.com
ServerAlias autodiscover.coragarden.com
...
DocumentRoot /var/www/mx.okbsd/html
<Directory /var/www/mx.okbsd/html/>
...
...
Alias /mail "/var/www/mx.okbsd/html/mail"
Alias /Autodiscover/Autodiscover.xml "/var/www/mx.okbsd/html/mail/Autodiscover.xml"
# ---
nano /var/www/mx.okbsd/html/index.php
# ---
<?php
print "<!DOCTYPE html lang=\"en\">
<html>
<head>
<title>OKBSD & CoraGarden Home Page</title></head>
<body>
<h1>Home Page</h1>
It works!!!
</body>
</html>\n";
?>
# ---
# Restart apache with the new configurations or the certbot request will fail
apachectl restart
# Test you can access the website certbot will challenge. I had used old apache syntax for allow,deny and it was
# failing. Once apache configuation was fixed everything worked as expected.
https://mx.coragarden.com
certbot certonly --webroot --agree-tos --redirect --hsts --staple-ocsp --email postmaster@okbsd.com --cert-name mx.okbsd.com \
-w /var/www/mx.okbsd/html -d mx.okbsd.com,mail.okbsd.com \
-w /var/www/mx.okbsd/html -d mx.coragarden.com,mail.coragarden.com
(U)pdate certificate/(C)ancel: U
Renewing an existing certificate for mx.okbsd.com and 3 more domains
# Restart apache
apachectl restart
# Test the new domain https://mx.coragarden.com/webmail/
# Login with the new account and test email
Login: vmail@coragarden.com
Password: ***********
# In my case the email was sent and recieved but no DKIM signature. I had forgot to add the new domain to
# the signingtable and do service milter-opendkim restart
nano /usr/local/etc/mail/signingtable
# ---
# Domain yourdomain.org
# *@okfigs.com 20250407._domainkey.okfigs.com
# You can specify multiple domains
# Example.net www._domainkey.example.net
*@okbsd.com CRHdAnOqqitUaWRuNkHLdIpbgw76._domainkey.okbsd.com
*@coragarden.com Cy9jmkzC2USPfcJ4JIuzA07rpB0gS._domainkey.coragarden.com
# ---
service milter-opendkim restart
# Check the source and the headers now have DKIM signature something like ...
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=coragarden.com;
s=Cy9jmkzC2USPfcJ4JIuzA07rpB0gS; t=1747783045;
bh=wiMQ3UTGd/zMN6+PeBSiAAUHXfKRtjj7J2UI7ZvJA+A=;
h=Date:From:To:Subject:From;
b=bkk87+VHY7HvSb1b9mq0b9bLc0XBMOzf6CnBMLcoLMAZ0yN1nDyrILNj9RiYUCJsq
hC6QsHu9S4t9Y8q85AoszhY78ddzfU8SLcg/IlTmWuiNWisrkKjAZ9ftPEtxVxkYKZ
VnpSveCK4O5Gw==
# Change password worked, but setting identity had an error in roundcube. Fix as follows.
# removed 'twofactor_gauthenticator', wasn't working right
cd /usr/local/www/roundcube/plugins/enigma
cp config.inc.php.dist config.inc.php
$config['enigma_pgp_homedir'] = "/usr/local/www/roundcube/plugins/enigma/home";
mkdir /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
pkg install gnupg