UPDATE : I’ve added a short section on configuring SSL and using acme.sh. In this post I describe how to set up a FAMP stack. A FAMP stack is a webserver running FreeBSD, Apache, Mysql, and PHP. For the purposes of this article, I’m substituting Mysql with Mariadb and mod_php with php-fpm.

I understand that the “How to set up a LAMP/FAMP stack” article is a dead horse that’s experienced continuous, unrelenting beatings for the last 20 years. I cannot stop the beatings so I’ve chosen to join in. Much like all other iterations of this topic, this article has been written exclusively for the author so that I might be able to use it my personalized reference guide in the future. Helping others is secondary.

Now you might be asking, “Why use FreeBSD when you could just use Linux?”. I raise this question in response: “Why use Linux when you could just use Windows or MacOS? Why use anything when you could just turn the computer off?”.

Do you choose to leave your computer on? Good, let’s get on with it.

Installing FreeBSD

This first step is very simple. bsdinstall is a curses based program that easily walks you through installation and setup. I strongly encourage you to choose ZFS as your root filesystem. Unless you know what you’re doing, UFS is only as reliable as your power company.

If you need help, go check out the FreeBSD Handbook. It’s an invaluable resource.

If your computer was manufactured in the last 15 years you probably need an amd64 installer image. Note that the .iso images won’t work if you’re trying to make a live USB flash drive (you’ll need an image with memstick in the name for that).

Alternatively, Digital Ocean offers easy, fully supported FreeBSD virtual private servers.

Installing Packages

First, we need to install the required packages. I’ll be using Apache, Mariadb, and php-fpm.

# first we tell pkg to install pkg 
$ pkg bootstrap && pkg update

# then we install required packages
$ pkg install php74 php74-mysqli apache24 mariadb103-server

Configuring Apache and PHP-FPM

Editing httpd.conf

After installing all the required packages, we need to edit /usr/local/etc/apache24/httpd.conf

Uncomment (ie remove the leading # from) the lines that look like the following:

#LoadModule mpm_event_module libexec/apache24/mod_mpm_event.so
#LoadModule proxy_module libexec/apache24/mod_proxy.so
#LoadModule proxy_fcgi_module libexec/apache24/mod_proxy_fcgi.so

Comment out (ie prepend a # to) the lines that look like the following:

LoadModule mpm_prefork_module libexec/apache24/mod_mpm_prefork.so

Save and quit this file.

Writing the Apache module for php-fpm.

Paste the following into /usr/local/etc/apache24/modules.d/030_php-fpm.conf:

<IfModule proxy_fcgi_module>
    <IfModule dir_module>
        DirectoryIndex index.php
    <FilesMatch "\.(php|phtml|inc)$">
        SetHandler "proxy:fcgi://"

Testing the configs

Edit /usr/local/www/apache24/data/phpinfo.php and add the following:


Now we’ll start our services and test everything to make sure it works so far.

$ service apache24 onestart
$ service php-fpm onestart

# run the following command
$ fetch http://localhost/phpinfo.php -q -o - | grep php-fpm 
# if it worked, you should see something like: 
# <tr><td class="e">php-fpm </td><td class="v">active </td></tr>

# now we delete phpinfo.php since it exposes information about our server
$ rm /usr/local/www/apache24/data/phpinfo.php 

Enabling services

When we enable a service it will automatically start at boot time. This is important because we don’t want to be monkeying around every single time we restart our server. Note that these configuration options are automatically appended to /etc/rc.conf.

$ service apache24 enable
$ service php-fpm enable

Configuring Mariadb

The first thing we need to do is start the mysql server. The first startup might take some time if you’re on potato hardware.

$ service mysql-server onestart

Oh no! This didn’t work! Mysqld is complaining! If we check the error file in /var/db/mysql/$HOST.err we can probably figure out what the problem is. Below are some examples of what my installation was complaining about:

[ERROR] InnoDB: Operating system error number 13 in a file operation.
[ERROR] InnoDB: The error means mysqld does not have the access rights to the directory.  

After some investigation, it appears that the mysql user wasn’t granted recursive permissions to the directory it’s working in. Classic, a DAC nightmare. Not to worry, we can fix this very easily.

# granting permissions
$ chown -R mysql:wheel /var/log/mysql/

# re-starting mysqld
$ service mysql-server onestart

# verifying that it's running
$ service mysql-server onestatus

# and now we do the secure installation
$ mysql_secure_installation

Enabling Mariadb

$ service mysql-server enable

The moment of truth

It’s time to reboot our system and check if everything persists across reboots.

# rebooting the system
$ reboot
# ssh back in . . . 
# . . . and verify that everything is running
$ ps aux | grep -e php -e apache -e mysql

Cool! Everything works for me!

Setting up SSL

This final step is mandatory your server sends or receives any user data. Although it’s optional in other cases, SSL really helps your website feel more professional. In coming years, I expect browsers to flag all unencrypted http traffic as “unsafe” and “insecure” so it’s best to get ahead of the curve.

I used to use let’s encrypt and certbot because It’s free. I still use let’s encrypt . . . but I no longer use certbot. Why? because certbot has extremely unpredictable behavior and tends to break my apache configs. Instead I’ve started to use acme.sh. acme.sh does not nuke the entirity of /etc so it’s much preferable to certbot.

1. Install acme.sh

$ pkg install acme.sh

2. Enable modules for SSL, vhosts, rewrite

Edit /usr/local/etc/apache24/httpd.conf and add the vhost Include. Uncomment (ie remove the leading #) from the lines that look like the following:

#LoadModule ssl_module libexec/apache24/mod_ssl.so
#LoadModule rewrite_module libexec/apache24/mod_rewrite.so
#LoadModule socache_shmcb_module libexec/apache24/mod_socache_shmcb.so
#Include etc/apache24/extra/httpd-vhosts.conf
#Include etc/apache24/extra/httpd-ssl.conf

3. Modify vhosts

Open /usr/local/etc/apache24/extra/httpd-vhosts.conf in your favorite editor. Add one vhost for each subdomain. The rewrite conditions are optional as certbot will offer to create them for us when we request certificates. See the apache docs on vhosts if you need more help.

<VirtualHost *:80> 
        DocumentRoot "/usr/local/www/apache24/data/your.domain"
        ServerName your.domain  
        ServerAlias www.your.domain 
        RewriteEngine on 
        RewriteCond %{SERVER_NAME} =your.domain [OR] 
        RewriteCond %{SERVER_NAME} =www.your.domain 
        RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent] 

4. Get let’s encrypt certs with acme.sh

First we generate certs . . .

$ acme.sh --issue -d your.domain --webroot /usr/local/www/apache24/data

Then we install them . . .

$ mkdir -p /usr/local/etc/apache24/ssl
$ cd /usr/local/etc/apache24/ssl
$  acme.sh --install-cert -d your.domain \ 
--cert-file         /usr/local/etc/apache24/ssl/your.domain-cert.pem \
--key-file          /usr/local/etc/apache24/ssl/your.domain-key.pem \
--fullchain-file    /usr/local/etc/apache24/ssl/letsencrypt-fullchain.pem \
--reloadcmd "service apache24 restart"

Then we write a cron job to automatically renew the certs every month . . .

0 0 1 * * acme.sh --renew -d your.domain --force

And now we’ll tell apache about them. Open /usr/local/etc/apache24/extra/httpd-ssl.conf in an editor. Find the default vhost block and insert your information. Mine looks something like:

# . . . 
<VirtualHost _default_:443>
DocumentRoot "/usr/local/www/apache24/data"
ServerName your.domain:443
ServerAdmin you@your.domain
ErrorLog "/var/log/httpd-error.log"
TransferLog "/var/log/httpd-access.log"
RewriteEngine on
SSLEngine on
SSLCertificateFile "/usr/local/etc/apache24/ssl/cert.pem"
SSLCertificateKeyFile "/usr/local/etc/apache24/ssl/key.pem"
SSLCertificateChainFile "/usr/local/etc/apache24/ssl/fullchain.pem"
# . . . 


Before slamming our head into our keyboard with absolute force, we must restart Apache.

$ service apache24 restart


Rinse, Repeat, and Refer to the docs if you’re still having trouble.