Setup Email Server From Scratch On FreeBSD #2 - 02 FAMP Install

01 Server Setup <- Intro -> 03 Postfix SMTPD

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.

#######################
# FAMP & Posfix Setup #
#######################

FAMP and LAMP stands for FreeBSD Apache MySQL PHP or Linux ... and are used to
provide dynamic website services with a database backend. They are required for
the mail stack and are used to run various suites like wordpress, phpmyadmin,
postfixadmin, and roundcube webmail. We'll set this up first before starting
on the actual mail stack installation.

pkg update
pkg upgrade

I have got this working with MariaDB before but the package version of opendmarc will try to remove MariaDB and install MySQL, so use the system default for compatiblility and simplicity of maintenance.

pkg install mysql80-server mysql80-client ghostscript10 netpbm

sysrc mysql_enable="YES"
service mysql-server start
service mysql-server status
mysql is running as pid 6690.

mysql --version
mysql Ver 8.0.42 for FreeBSD14.2 on amd64 (Source distribution)

If the root account is secure the root@localhost password can be left blank (the default). Optionally setup an admin account with a password.

mysql
> ALTER USER 'root'@'localhost' IDENTIFIED WITH caching_sha2_password BY 'secretpasswd'; > GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION; > flush privileges; > CREATE USER 'admin'@'localhost' IDENTIFIED WITH caching_sha2_password BY 'secretpasswd'; > GRANT ALL PRIVILEGES ON *.* TO 'admin'@'localhost'; > flush privileges; > exit

Tune mysql server for higher efficiency, 32G memory available with 28 free.

cd /usr/local/etc/mysql
cp my.cnf my.cnf.bak

nano /usr/local/etc/mysql/my.cnf
--- edit or add ---
[mysqld] ... innodb_buffer_pool_size = 12G innodb_log_file_size = 1G max_connections = 500 tmp_table_size = 128M max_heap_table_size = 128M sort_buffer_size = 2M slow_query_log = 1 long_query_time = 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
service mysql-server status

If it doesn't restart check the logs, do not use skip-name-resolve.

tail -f /var/db/mysql/okbsd.com.err

##################
# INSTALL APACHE #
##################

pkg install apache24
sysrc apache24_enable="YES"
service apache24 start
service apache24 status

Use a web browser and go to ...
http://okbsd.com
It Works!

Install perl PHP module
pkg install p5-DBD-mysql

Install PHP and PHP modules
pkg install php83 mod_php83 php83-mysqli php83-curl php83-zip php83-gd php83-xml php83-mbstring
pkg install php83-bcmath php83-tokenizer php83-zlib php83-imap php83-bz2
pkg install php83-pecl-imagick php83-ldap php83-intl php83-gmp php83-pecl-redis

Configure php - these values will affect max attachment size in Roundcube

ls -l /usr/local/etc/php.ini

If it doesn't exist copy the production template.

cp /usr/local/etc/php.ini-production /usr/local/etc/php.ini

nano /usr/local/etc/php.ini
max_execution_time = 600 max_input_time = 300 max_input_vars = 3000 memory_limit = 256M post_max_size = 400M upload_max_filesize = 200M date.timezone = "America/Los_Angeles"

sysrc php_fpm_enable="YES"
service php_fpm start
service php_fpm status

apachectl restart

sockstat | grep 9000
www php-fpm 29241 8 tcp4 127.0.0.1:9000 *:* www php-fpm 29240 8 tcp4 127.0.0.1:9000 *:* root php-fpm 29239 7 tcp4 127.0.0.1:9000 *:*

Backup Apache configuation.

cd /usr/local/etc/apache24
cp /usr/local/etc/apache24/httpd.conf /usr/local/etc/apache24/httpd.conf.default
cp /usr/local/etc/apache24/httpd.conf /usr/local/etc/apache24/httpd.conf.bak

Apache Configuration - choose built in php or php-fpm, you do not need both

Two host definitions in separate locations might yield unexpected results.

In FreeBSD the httpd.conf set ServerName to 127.0.0.1 and in ssl module options set the vitual host host to 127.0.0.1, so these configurations will not be used except for local connections. Or, remove the Virtual Host section in /usr/local/etc/apache24/extra/httpd-ssl.conf and define _default_ in an include file.

Then define real virtual hosts in include files.

nano /usr/local/etc/apache24/httpd.conf
Listen 80 LoadModule socache_shmcb_module libexec/apache24/mod_socache_shmcb.so LoadModule logio_module libexec/apache24/mod_logio.so LoadModule proxy_module libexec/apache24/mod_proxy.so LoadModule proxy_fcgi_module libexec/apache24/mod_proxy_fcgi.so LoadModule ssl_module libexec/apache24/mod_ssl.so <IfModule !mpm_prefork_module> LoadModule cgid_module libexec/apache24/mod_cgid.so </IfModule> <IfModule mpm_prefork_module> LoadModule cgi_module libexec/apache24/mod_cgi.so </IfModule> LoadModule speling_module libexec/apache24/mod_speling.so LoadModule rewrite_module libexec/apache24/mod_rewrite.so # For builtin php # LoadModule php_module libexec/apache24/libphp.so ServerAdmin postmaster@okbsd.com ServerName 127.0.0.1 # change your paths to match your server file location DocumentRoot "/usr/local/www/okbsd/html" <Directory "/usr/local/www/okbsd/html"> # Options -Indexes -MultiViews +FollowSymLinks Options -Indexes -MultiViews +SymLinksIfOwnerMatch <IfModule dir_module> DirectoryIndex index.php index.html </IfModule> # Add a vhost_combined log format to view by virtual hosts %v <IfModule log_config_module> ... # LogFormat "%v:%p %h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" vhost_combined ... CustomLog "/var/log/httpd-access.log" vhost_combined </IfModule> # If you want to run cgi's enable the following 3 sections and the handler <IfModule alias_module> .... # ScriptAlias /cgi-bin/ "/usr/local/www/apache24/cgi-bin/" ScriptAlias /cgi-bin/ "/var/www/okbsd/cgi-bin/" </IfModule> <Directory "/var/www/okbsd/cgi-bin"> AllowOverride None # Options ExecCGI Options +ExecCGI -Indexes -MultiViews +SymLinksIfOwnerMatch Require all granted </Directory> # If you want cgi support, farther down in the file in mime_module AAddHandler cgi-script .cgi # Optional Apache performance tuning, values here are experimental # and depend on the resources available. These can go after # ErrorDocument settings # see mpm config file # StartServers 40 # MinSpareServers 40 # MaxSpareServers 60 KeepAlive Off MaxKeepAliveRequests 20 KeepAliveTimeout 5 ServerLimit 512 MaxClients 512 MaxRequestsPerChild 60 # Do enable the following, not so optional. # Supplemental configuration ... # Server-pool management (MPM specific) Include etc/apache24/extra/httpd-mpm.conf Include etc/apache24/Includes/*.conf

Optional Apache mpm fine tuning if more performance is needed, values here are experimental and depend on the resources available.

nano /usr/local/etc/apache24/extra/httpd-mpm.conf
<IfModule mpm_prefork_module> StartServers 20 MinSpareServers 20 MaxSpareServers 30 MaxRequestWorkers 250 MaxConnectionsPerChild 150 </IfModule>

You will need either libphp module or php-fpm module.

nano /usr/local/etc/apache24/modules.d/200_mod-php.conf
<IfModule dir_module> DirectoryIndex index.php index.html <FilesMatch "\.php$"> # libphp module # SetHandler application/x-httpd-php # php-fpm module SetHandler "proxy:fcgi://127.0.0.1:9000" </FilesMatch> <FilesMatch "\.phps$"> SetHandler application/x-httpd-php-source </FilesMatch> </IfModule>

Optional fine tuning for php-fpm only, values here are experimental.

nano /usr/local/etc/php-fpm.d/www.conf
pm=static pm.max_children=20 pm.max_requests = 1000 slowlog = /var/log/$pool.log.slow request_slowlog_timeout = 30 request_terminate_timeout = 600

service php_fpm restart

Create website directories and a test php

mkdir -p /usr/local/www/okbsd/html
mkdir -p /usr/local/www/okbsd/mail
echo '<?php print "<!DOCTYPE html lang=\"en\"><html><head><title>Title</title></head><body><h1>Hello World!</h1></body></html>"; ?>' > /usr/local/www/okbsd/html/index.php
echo '<?php phpinfo(); ?>' > /usr/local/www/okbsd/html/info.php

chown root:wheel /usr/local/www
chmod 755 /usr/local/www
chown -R root:www /usr/local/www/okbsd
find /usr/local/www/okbsd -type d -exec chmod 750 {} \;
find /usr/local/www/okbsd -type f -exec chmod 640 {} \;

apachectl configtest
apachectl restart
apachectl status

Check that Apache and PHP are working.

http://okbsd.com/index.php
http://okbsd.com/info.php

Create logs executable - an easy wasy to view apache errors

nano ~/bin/logs
#/bin/sh clear; echo '' > /var/log/httpd-error.log; tail -f /var/log/httpd-error.log

chmod 750 ~/bin/logs

To see errors use the 'logs' command you created in ~/bin,

logs
<refresh your webpage to generate new errors>
<view the errors>
<control-c to exit>

Configure websites in Apache Configuration - In FreeBSD the default location for website files is /usr/local/www/apache24/data but to keep the path short and reduce typing I make a symbolic link and use /var/www/sitename/data.

nano /usr/local/etc/apache24/Includes/okbsd.conf
<VirtualHost _default_:80> ServerName okbsd.com ServerAlias www.okbsd.com ServerAlias 147.135.37.135 ServerAlias [2604:2dc0:200:187::1] 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> # # If you want to run cgi's uncomment these and the handler ScriptAlias /cgi-bin/ "/usr/local/www/okbsd/cgi-bin/" <Directory "/usr/local/www/okbsd/cgi-bin"> AllowOverride None Options +ExecCGI -Indexes -MultiViews +SymLinksIfOwnerMatch Require all granted </Directory> </VirtualHost>

nano /usr/local/etc/apache24/Includes/mail.okbsd.conf
<VirtualHost *:80> 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/" <Directory /usr/local/www/okbsd/mail/> Options FollowSymLinks AllowOverride All Require all granted </Directory> #RewriteEngine on #RewriteCond %{SERVER_NAME} =mail.okbsd.com [OR] #RewriteCond %{SERVER_NAME} =mx.okbsd.com #RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent] </VirtualHost>

apachectl configtest
apachectl restart
apachectl status

Check that Apache and PHP are working.

http://okbsd.com/index.php
http://okbsd.com/info.php

Remove the info.php script for security

rm /usr/local/www/okbsd/html/info.php

##########################
# Setup SSL with certbot #
##########################

Certbot needs a virtual host setup or it won't be able to generate the certificates.

pkg install py311-certbot-apache

When running certbot you may get a warning ... certs can still be created but
cron could have problems renewing certs.

Unable to read ssl_module file; not disabling session tickets.

Add the following symbolic link to hide the warning.

ln -s /usr/local/libexec/apache24 /usr/local/etc/apache24/libexec/apache24

To simplify management create on certificate with all 5 hostnames. For granular control, security, and an insigificant performance increase create separate certs for each service. With a combined cert web users can view the alternate imap and smtp hostnames in the cert. If this is not desirable create a separate cert for these 2 hosts or 1 cert for each host (granualar). Obscurity is not security so balance service separation with easy of management with 2 certificates 1) web services# 2) mail and webmail services.

certbot certonly --apache --agree-tos --redirect --hsts --staple-ocsp --email postmaster@okbsd.com --cert-name okbsd.com -d okbsd.com,www.okbsd.com

Create a certificate for mail services, and mail. web services.
certbot certonly --apache --agree-tos --redirect --hsts --staple-ocsp --email postmaster@okbsd.com --cert-name mail.okbsd.com -d mail.okbsd.com,smtp.okbsd.com,imap.okbsd.com

Secure private key certificate files.

chown -R root:www /usr/local/etc/letsencrypt/live
find /usr/local/etc/letsencrypt/live -type d -exec chmod 755 {} \;
find /usr/local/etc/letsencrypt/live -type f -exec chmod 644 {} \;

chown -R root:www /usr/local/etc/letsencrypt/archive
find /usr/local/etc/letsencrypt/archive -type d -exec chmod 755 {} \;
find /usr/local/etc/letsencrypt/archive -type f -exec chmod 644 {} \;
chmod o-rwx /usr/local/etc/letsencrypt/archive/*/privkey*.pem

Setup a crontab to run certbot and renew all your certs...

crontab -e
0 2 * * 1 /usr/local/bin/certbot renew --quiet && apachectl restart

To list certificates
certbot certificates

To revoke and delete certificates
certbot revoke --cert-name <certname>
certbot delete --cert-name <certname>

If you leave out 'certonly', certbot may modify your configuration files and add ssl configuration files based on the non-ssl configuration. Generally it is better to use 'certonly' and manage your own settings. Certbot without 'certonly' adds Rewrite rules to okbsd.conf to redirect all non ssl requests to ssl and adds includes to httpd.conf.

Enable Apache SSL/HTTPS

nano /usr/local/etc/apache24/httpd.conf
# Secure (SSL/TLS) connections Include etc/apache24/extra/httpd-ssl.conf

make a backup
cd /usr/local/etc/apache24/extra
cp /usr/local/etc/apache24/extra/httpd-ssl.conf /usr/local/etc/apache24/extra/httpd-ssl.conf.bak

nano /usr/local/etc/apache24/extra/httpd-ssl.conf
SSLRandomSeed startup file:/dev/urandom 512 SSLRandomSeed connect file:/dev/urandom 512 ... <VirtualHost 127.0.0.1:443> DocumentRoot "/usr/local/www/okbsd/html" ServerName okbsd.com:443 ServerAdmin postmaster@okbsd.com ErrorLog "/var/log/httpd-error.log" TransferLog "/var/log/httpd-access.log" SSLCertificateFile /usr/local/etc/letsencrypt/live/okbsd.com/fullchain.pem SSLCertificateKeyFile /usr/local/etc/letsencrypt/live/okbsd.com/privkey.pem ... CustomLog "/var/log/httpd-ssl_request.log" \ "%v:%p %h %l %u %t %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"

/usr/local/etc/apache24/Includes/ssl-okbsd.conf
<IfModule mod_ssl.c> SSLStaplingCache shmcb:/var/run/apache2/stapling_cache(128000) <VirtualHost _default_:443> ServerName okbsd.com ServerAlias www.okbsd.com ServerAlias 147.135.37.135 ServerAlias [2604:2dc0:200:187::1] ServerAdmin postmaster@okbsd.com TransferLog "/var/log/httpd-access.log" CustomLog "/var/log/httpd-ssl_request.log" \ "%v:%p %h %l %u %t %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b" 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> # If you want to run cgi's uncomment these and the handler ScriptAlias /cgi-bin/ "/usr/local/www/okbsd/cgi-bin/" <Directory "/usr/local/www/okbsd/cgi-bin"> AllowOverride None Options +ExecCGI -Indexes -MultiViews +SymLinksIfOwnerMatch Require all granted </Directory> Include /usr/local/etc/letsencrypt/options-ssl-apache.conf SSLCertificateFile /usr/local/etc/letsencrypt/live/okbsd.com/fullchain.pem SSLCertificateKeyFile /usr/local/etc/letsencrypt/live/okbsd.com/privkey.pem Header always set Strict-Transport-Security "max-age=31536000" </VirtualHost> </IfModule>

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/" <Directory /usr/local/www/okbsd/mail/> Options FollowSymLinks AllowOverride All Require all granted </Directory> #RewriteEngine on #RewriteCond %{SERVER_NAME} =mail.okbsd.com [OR] #RewriteCond %{SERVER_NAME} =mx.okbsd.com #RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent] 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 configtest
apachectl restart

test it
https://okbsd.com
https://www.okbsd.com
https://mail.okbsd.com

###########################################################################################
# Postfix is a popular smtp server and we'll continue with postfix setup on the next page #
###########################################################################################

01 Server Setup <- Intro -> 03 Postfix SMTPD