Setup Email Server From Scratch On FreeBSD #2 - 02 LAMP Install
We believe in data independence, and support others who want data independence.
Debian Email From Scratch version 2 finished 2025-07-30.
We are still adding to it but it all works!
############## # FAMP Setup # ##############
LAMP stands for Linux Apache MySQL PHP ... a stack of servers and languages used
to provide dynamic database driven website services. They are required for the
mail stack and are used to run postfixadmin, and roundcube webmail. We'll set
this up first before starting on the actual mail stack installation.
################### # INSTALL MARIADB # ###################apt update -y && apt upgrade -y
apt-get install mariadb-server mariadb-client
systemctl enable mariadb
systemctl start mariadb
systemctl status mariadb
mysql_secure_installation
You can leave the mysql root password blank if you are concerned it might be
forgotten but then the database is only as secure as the root account. In
this case it is a good idea to use unix socket and disallow root remotely.
Do not restrict mysql to unix socket if remote access is needed
Do restrict root login to local connections
If you do not have any untrusted users on the system you do not need reset
default root password and can leave it blank.
Switch to unix_socket authenitcaiton: n
Change root password: n
Remove anonymous users: Y
Disallow root login remotely: Y
Remove test database and access to it: Y
Reload privilege tables now: Y
Login as root user, for the full command there is no space after -p and before the password
If your password was blank just use 'mysql'
mysql -u root -p<myqlrootpassword>
mysql
Create an admin account with a password as below, replace secretpassword with your password
GRANT ALL ON *.* TO 'admin'@'localhost' IDENTIFIED BY 'secretpassword' WITH GRANT OPTION;
FLUSH PRIVILEGES;
You can use either
For collation general_ci is faster but may not sort as well for all languates, unicode_520 is more precise
collation-server = utf8mb4_unicode_520_ci
collation-server = utf8mb4_general_ci
nano /usr/local/etc/mysql/conf.d/server.cnf
[server]
lower_case_table_names=1
character-set-server = utf8mb4
# collation-server = utf8mb4_unicode_520_ci
collation-server = utf8mb4_general_ci
# this is only for the mysqld standalone daemon
[mysqld]
lower_case_table_names=1
..... farther down
character-set-server = utf8mb4
# collation-server = utf8mb4_unicode_520_ci
collation-server = utf8mb4_general_ci
... farther ...
[mariadb]
lower_case_table_names=1
character-set-server = utf8mb4
# collation-server = utf8mb4_unicode_520_ci
collation-server = utf8mb4_general_ci
systemctl restart mariadb
Here is how to create a database and a user with access to only that database
mysql
create database mydatabase;
GRANT ALL ON mydatabase.* TO 'myuser'@'localhost' IDENTIFIED BY 'mysecretpassword' WITH GRANT OPTION;
flush privileges;
systemctl restart mariadb
INSTALL PERL DBD::mysql - usefull if running perl scripts or perl cgi's that need access to modify or repair
the database. If you only write php scripts this is optional.
apt-get install libdbd-mysql-perl
INSTALL APACHE WEB SERVER
apt-get install apache2 -y
systemctl start apache2
systemctl enable apache2
systemctl status apache2
INSTALL PHP - minimal install
apt install php libapache2-mod-php php-cli php-fpm php-json php-mysql php-zip
apt install php-gd php-mbstring php-curl php-xml php-pear php-bcmath
Configure PHP /etc/php/8.2/apache2/php.ini /etc/php/8.2/cli/php.ini
If your PHP version is different replace 8.2 with current version below...
These values can affect max upload and file size when sending attachments with roundcube.
Use fpm only if using php with nginx, we are using apache not nginx.
nano /etc/php/8.2/apache2/php.ini
nano /etc/php/8.2/cli/php.ini
max_execution_time = 600
max_input_time = 1000
max_input_vars = 3000
memory_limit = 256M
post_max_size = 48M
upload_max_filesize = 32M
a2enconf php8.2-fpm
a2enmod proxy_fcgi setenvif rewrite ssl
if you want cgi enabled
a2enmod cgi
systemctl reload apache2
Use a web browser and go to ...
http://okdeb.com
It Works!
Make a virtual hosts for website and for mail hosts, 2 configuration files 4 virtual containers.
Create web server directories and test files
mkdir -p /var/www/okdeb/html
mkdir -p /var/www/okdeb/mail
mkdir -p /var/www/okdeb/mx
mkdir -p /var/www/okdeb/cgi-bin
echo '<?php print "<!DOCTYPE html lang=\"en\"><html><head><title>It Works!</title></head><body><h1>PHP Works!</h1></body></html>"; ?>' > /var/www/okdeb/html/test.php
echo '<?php print "<!DOCTYPE html lang=\"en\"><html><head><title>It Works!</title></head><body><h1>PHP Works!</h1></body></html>"; ?>' > /var/www/okdeb/mail/test.php
echo '<?php print "<!DOCTYPE html lang=\"en\"><html><head><title>It Works!</title></head><body><h1>PHP Works!</h1></body></html>"; ?>' > /var/www/okdeb/mx/test.php
echo '<?php phpinfo(); ?>' > /var/www/okdeb/html/info.php
chmod 750 /var/www/okdeb
chmod 750 /var/www/okdeb/html
chmod 750 /var/www/okdeb/mail
chmod 750 /var/www/okdeb/mx
chmod 750 /var/www/okdeb/cgi-bin
chmod -R o-rwx /var/www/okdeb/html
chmod -R o-rwx /var/www/okdeb/mail
chmod -R o-rwx /var/www/okdeb/mx
chmod -R o-rwx /var/www/okdeb/cgi-bin
chown -R root:www-data /var/www/okdeb
These VirtualHost definitions can be separated or combined with all 4 VirtualHost
definitions together. With a few hosts separate files would be better, but with
a large number of virtual hosts fewer files is preferable.
A valid http configuration is needed to generate lecerts, and apache in won't run
in https mode without the certs. Setup the virtual http hosts, generate the
letsencrypt certificates, then enable https virtual hosts with the certificates.
cd /etc/apache2/sites-available
nano /etc/apache2/sites-available/okdeb.conf
<VirtualHost _default_:80>
ServerName okdeb.com
ServerAlias www.okdeb.com
ServerAlias 15.204.113.148
ServerAlias [2604:2dc0:202:300::3645]
ServerAdmin postmaster@okdeb.com
CustomLog ${APACHE_LOG_DIR}/access.log vhost_combined
ErrorLog ${APACHE_LOG_DIR}/error.log
DirectoryIndex index.php index.html
DocumentRoot /var/www/okdeb/html/
<Directory /var/www/okdeb/html/>
Options FollowSymLinks
AllowOverride All
Require all granted
</Directory>
# # If you want to run cgi's uncomment these and the handler
ScriptAlias /cgi-bin/ "/var/www/okdeb/cgi-bin/"
<Directory "/var/www/okdeb/cgi-bin">
AllowOverride None
Options +ExecCGI -Indexes -MultiViews +SymLinksIfOwnerMatch
Require all granted
</Directory>
</VirtualHost>
nano /etc/apache2/sites-available/mx.okdeb.conf
<VirtualHost *:80>
ServerName mx.okdeb.com
ServerAlias mail.okdeb.com
ServerAlias autoconfig.okdeb.com
ServerAlias autodiscover.okdeb.com
ServerAdmin postmaster@okdeb.com
CustomLog ${APACHE_LOG_DIR}/access.log vhost_combined
ErrorLog ${APACHE_LOG_DIR}/error.log
DirectoryIndex index.php index.html
DocumentRoot /var/www/okdeb/mx/
<Directory /var/www/okdeb/mx/>
Options FollowSymLinks
AllowOverride All
Require all granted
</Directory>
Alias /mail "/var/www/okdeb/mail/"
<Directory /var/www/okdeb/mail/>
Options FollowSymLinks
AllowOverride All
Require all granted
</Directory>
#RewriteEngine on
#RewriteCond %{SERVER_NAME} =mail.okdeb.com [OR]
#RewriteCond %{SERVER_NAME} =mx.okdeb.com
#RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
</VirtualHost>
cd /etc/apache2/sites-enabled
rm /etc/apache2/sites-enabled/000-default.conf
rm /etc/apache2/sites-enabled/default-ssl.conf
ln -s ../site-available/okdeb.conf
ln -s ../site-available/mx.okdeb.conf
apachectl restart
Test
http://okdeb.com/test.php
http://okdeb.com/info.php
http://mail.okdeb.com/test.php
http://mx.okdeb.com/mail/test.php
########################## # Setup SSL with certbot # ##########################you will need a virtual host for certbot
apt install certbot python3-certbot-apache
Certbot needs a virtual host setup for the domain, but we added that above.
Certbot may need module rewrite to be enabled.
Cron did not appear to be installed and certbot will need cron
apt-get install cron
systemctl status cron
systemctl enable cron.service
systemctl start cron.service
Create certificates for web and mail hosts, each cert has 2 hosts
New versions of postfix and Dovecot support SNI, server name indication so
we'll set this up in case we want to host multiple mail domains.
Here are some suggested names ...
<ul>
<li> mail.domain.tld
<li> imap.domain.tld
<li> smtp.domain.tld
<li> mx.domain.tld
<li> mx1.domain.tld
<li> mx1.domain.tld
</ul>
Autoconfig and autodiscover are used to help mail clients automatically
discover mail server setttings. The autodiscover SRV records must be on a
https web host amd can be directly to the main mail servers hostname. We'll
setup autoconfig and autodiscover at the end of setting up Dovecot and
Thunderbird mail client but thinking ahead we'll generate the certs and setup
the directories now.
If website and mail web services are put together it can reduce the number of
directories and certs. Separating regular web and mail services allows
administrators to change one without breaking the other. It is good to reduce
the attack surface somewhat by keeping things like postfixadmin and roundcube
out of the main website structure and only enabling https.
You could make just one certificate for everything, or separate as shown here.
The simplest solution is to point all the MX records for multiple domains to
one or two "main" hostnames. To host multiple virtual domains with a cert for
only 2 hostnames set the MX records in the virtual domain(s) to the
hostname(s) of the primary mail server. The A and AAAA records for these mx
hosts can be the same or separate machines, we'll add a second here pointing
to the same IPv4 and IPv6 address as a placeholder for setting up a backup mx
server at a later date. The second MX record pointing to the same server also
reduces delivery delay when postgrey and postscreen spam filtering and enabled.
Example DNS for okdeb.com - mail sent to user@okdeb.com
MX @ mx.okdeb.com 0
MX @ mail.okdeb.com 10
Example DNS for domain2.com - mail sent to user@domain2.com
MX @ mx.okdeb.com 0
MX @ mail.okdeb.com 10
Example DNS for domain3.net - mail sent to user@domain3.net
MX @ mx.okdeb.com 0
MX @ mail.okdeb.com 10
In this case you could ... and use this one cert for everything, eg in apache
configuation and /etc/postfix/main.cf and /etc/dovecot/conf.d/10-ssl.conf
certbot certonly -a apache --agree-tos --redirect --hsts --staple-ocsp --email postmaster@okdeb.com --cert-name okdeb.com -d okdeb.com,www.okdeb.com,mx.okdeb.com,mail.okdeb.com,autodiscover.okdeb.com
You only have one mail host, 2 MX records, and 1 certificate for everything. Autoconfig
and autodiscover point to one mx.host and only this host shows up in the imap and
smtp connection parameters for the mail client(s).
To use separate mx hosts each domain, mx records will be pointed to thier own
mx hostname. These MX hosts will also need their own A and AAAA records. In
this case the postfix and dovecot cert will need all mx hostnames for all
domains serviced. Customers like this option better as each domain is personalized,
but setup is more complicated.
Example DNS for okdeb.com - mail sent to user@okdeb.com
MX @ mx.okdeb.com 0
MX @ mail.okdeb.com 10
Example DNS for domain2.com - mail sent to user@domain2.com
MX @ mx1.domain2.com 0
MX @ mx2.domain2.com 10
Example DNS for domain3.net - mail sent to user@domain3.net
MX @ imap.domain3.net 0
MX @ smtp.domain3.net 10
This only affects what hostnames show up in the mail client, eg Thunderbird under
connection settings. Autoconfigure can be customized for each domain.
Configure for separate mx hosts for each virtual domain, start with the first
mx host. We have switch from using a common cert to using SNI for postfix and
dovecot. We'll cover virtual domains later.
Make regular website cert
certbot certonly -a apache --agree-tos --redirect --hsts --staple-ocsp --email postmaster@okdeb.com --cert-name okdeb.com -d okdeb.com,www.okdeb.com
Make cert for postfix, dovecot, autodiscoer, and mx/mail website.
certbot certonly -a apache --agree-tos --redirect --hsts --staple-ocsp --email postmaster@okdeb.com --cert-name mx.okdeb.com -d mx.okdeb.com,mail.okdeb.com,autodiscover.okdeb.com
When you want to add or remove hosts from a cert rerun certbot without the unwanted
hostname or with the new hostnames, certbot will prompt to update the cert.
certbot certonly -a apache --agree-tos --redirect --hsts --staple-ocsp --email postmaster@okdeb.com --cert-name mx.okdeb.com -d mx.okdeb.com,smtp.okdeb.com,imap.okdeb.com,autodiscover.okdeb.com
List certificates
certbot certficates
Revoke and delete certificates
certbot revoke --cert-name <certname>
certbot delete --cert-name <certname>
Make sure the auto renew script is being run by cron
cat /etc/cron.d/certbot
Setup SSL for website and mail website
nano /etc/apache2/sites-available/ssl-okdeb.conf
<VirtualHost _default_:443>
ServerName okdeb.com
ServerAlias www.okdeb.com
ServerAlias 15.204.113.148
ServerAlias [2604:2dc0:202:300::3645]
ServerAdmin postmaster@okdeb.com
CustomLog ${APACHE_LOG_DIR}/access.log vhost_combined
ErrorLog ${APACHE_LOG_DIR}/error.log
DirectoryIndex index.php index.html
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/okdeb.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/okdeb.com/privkey.pem
DocumentRoot /var/www/okdeb/html/
<Directory /var/www/okdeb/html/>
Options FollowSymLinks
AllowOverride All
Require all granted
</Directory>
<FilesMatch "\.(?:cgi|shtml|phtml|php)$">
SSLOptions +StdEnvVars
</FilesMatch>
# If you want cgi
ScriptAlias /cgi-bin/ "/var/www/okdeb/cgi-bin/"
<Directory "/var/www/okdeb/cgi-bin">
# AllowOverride All
AllowOverride None
Options +ExecCGI -Indexes -MultiViews +SymLinksIfOwnerMatch
Require all granted
</Directory>
</VirtualHost>
nano /etc/apache2/sites-available/ssl-mx.okdeb.conf
<VirtualHost *:443>
ServerName mx.okdeb.com
ServerAlias mail.okdeb.com
ServerAlias autoconfig.okdeb.com
ServerAlias autodiscover.okdeb.com
ServerAdmin postmaster@okdeb.com
CustomLog ${APACHE_LOG_DIR}/access.log vhost_combined
ErrorLog ${APACHE_LOG_DIR}/error.log
DirectoryIndex index.php index.html
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/mx.okdeb.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/mx.okdeb.com/privkey.pem
DocumentRoot /var/www/okdeb/mx/
<Directory /var/www/okdeb/mx/>
Options FollowSymLinks
AllowOverride All
Require all granted
</Directory>
Alias /mail "/var/www/okdeb/mail"
Alias "/Autodiscover/Autodiscover.xml" "/var/www/okdeb/mail/Autodiscover.xml/index.php"
<Directory /var/www/okdeb/mail/>
DirectorySlash Off
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
</Directory>
<FilesMatch "\.(?:cgi|shtml|phtml|php)$">
SSLOptions +StdEnvVars
</FilesMatch>
<Directory /var/www/okdeb/cgi-bin>
SSLOptions +StdEnvVars
</Directory>
</VirtualHost>
Enable https hosts and restart apache
cd /var/apache2/site-enabled
ln -s ../sites-available/ssl-okdeb.conf
ln -s ../sites-available/ssl-mx.okdeb.conf
apachectl restart
Test - we test https://mx.okdeb.com/mail to confirm php is enabled for Autodiscover.xml/index.php
https://okdeb.com/test.php
https://okdeb.com/info.php
https://mx.okdeb.com/test.phyp
https://mx.okdeb.com/mail/test.php
For security remove the php script
rm /var/www/okdeb/html/info.php
It is a good idea to remove the other php scripts as well before going live.
/var/www/okdeb/html/test.php
/var/www/okdeb/mail/test.php
/var/www/okdeb/mx/test.php
Optionally create and test cgi
nano /var/www/okdeb/cgi-bin/test.cgi
#!/usr/bin/perl
print "Content-type: text/html\n\n";
print "Hello this a cgi script that can't parse variables.\n";
chmod 755 /var/www/okdeb/cgi-bin/test.cgi
Test your cgi if using perl cgi's
http://okdeb.com/cgi-bin/test.cgi
Remove it for security
rm /var/www/okdeb/cgi-bin/test.cgi
Postfix is a popular smtp server and we'll continue with postfix setup on the next page...