Create a FreeBSD web server (FAMP stack)

FAMP stack? That’s FreeBSD + Apache + MariaDB + PHP.

Here’s the steps necessary to build an Apache web server with MariaDB on FreeBSD.

Before we begin:

  • This guide is meant to supplement, but not override, the FreeBSD handbook, which should be considered the canonical documentation source for FreeBSD.
  • This guide was written in January 2023 and applies to FreeBSD 12 and 13. If you found this guide from the future, please be aware these instructions may be outdated.
  • This guide assumes you have some basic experience with Linux/BSD servers.

Table of contents

  1. Find hosting and provision an instance
  2. Set up the machine
  3. Simplify outbound email handling
  4. Install a web server (Apache + PHP)
  5. Install a database server (MariaDB)
  6. Final steps
  7. Tips and tricks

Step 1. Find hosting and provision an instance

You can create FreeBSD instances on:

  • AWS Lightsail
  • AWS EC2
  • Vultr

FreeBSD is also supported on Linode and DigitalOcean, but requires some extra steps in order to get an instance up and running.

Choose a provider and build an instance. If you’re not sure what size to get, start small. A $5/mo AWS Lightsail instance (1 GB memory, 1 core CPU, 40 GB SSD) is more than sufficient to host several small WordPress sites. Static content caching will reduce load on the server and a CDN will reduce I/O load even further.

Step 2. Set up the machine

Update the system

Once the machine is provisioned, log in as root and update the system.

First, update the FreeBSD core.

# freebsd-update fetch install

Then, update the non-core software packages.

# pkg update
# pkg upgrade

Set the timezone

# tzsetup

Install some useful applications

# pkg install sudo wget git bash bash-completion tmux htop ncdu lsblk p5-ack

Helpful hint: Be careful when installing vim. Sometimes you might accidentally choose the X11 graphical version, which will load a ton of packages that you don’t need (and don’t want) running on a web server. If you run this command and see the total install size is over 15 MB and includes dozens of packages (like gtk3, wayland, adwaita-icon-theme, or lots of libX___ packages) you’re probably installing the wrong one.

# pkg install vim

Set up sudo access

Allow users in the wheel group to use sudo. Run visudo and uncomment the following line:

%wheel   ALL=(ALL)   ALL

Set the hostname

Verify that your hostname looks appropriate.

# hostname

If you need to change it, edit /etc/rc.conf and modify the hostname="" line. Then, run sudo hostname [your-hostname] to update the hostname without rebooting. Validate your work again by running hostname.

Create a swapfile

Some VPS’s don’t create swapfiles when the instances are provisioned. It’s a good idea to create one, even a small one (512-1,024 MB).

First, create the swapfile and set permissions on it.

# dd if=/dev/zero of=/usr/swap0 bs=1m count=512
# chmod 600 /usr/swap0

Then, edit /etc/fstab and add this line:

md99   none   swap   sw,file=/usr/swap0,late   0   0

Next, activate the swapfile with:

# swapon -aL

Create your non-root user account

Create a user account for yourself.

# adduser

You’ll need to answer a dozen or so questions during this process. Simply press [Enter] unless you’re at one of these prompts:

  1. Select a username (should be all lowercase, no symbols or spaces).
  2. Provide the user’s full name.
  3. Invite the user into the wheel group so they can use sudo.
  4. Choose bash if you want.
  5. Provide a password, and enter it twice to make sure it’s entered correctly.
  6. Review the entries.
  7. Stop adding users if you’re done.

The process will look like this:

Username: btorres                                                       <--- (1)
Full name: B'Elanna Torres                                              <--- (2)
Uid (Leave empty for default):
Login group [btorres]:
Login group is btorres. Invite btorres into other groups? []: wheel     <--- (3)
Login class [default]:
Shell (sh csh tcsh bash rbash git-shell nologin) [sh]:                  <--- (4)
Home directory [/home/btorres]:
Home directory permissions (Leave empty for default):
Use password-based authentication? [yes]:
Use an empty password? (yes/no) [no]:
Use a random password? (yes/no) [no]:
Enter password:                                                         <--- (5)
Enter password again:                                                   <--- (5)
Lock out the account after creation? [no]:
Username   : btorres
Password   : *****
Full Name  : B'Elanna Torres
Uid        : 1002
Class      :
Groups     : btorres wheel
Home       : /home/btorres
Home Mode  :
Shell      : /bin/sh
Locked     : no
OK? (yes/no): yes                                                       <--- (6)
adduser: INFO: Successfully added (btorres) to the user database.
Add another user? (yes/no): no                                          <--- (7)
Goodbye!

If you want to change an existing user’s shell to bash:

# chsh -s /usr/local/bin/bash [username]

And then add this to the bottom of ~/.profile so that the system will parse ~/.bashrc on login:

# Load .bashrc on login
if [[ $- == *i* && -f ~/.bashrc ]]; then
    . ~/.bashrc
fi

Important note: Do not change root’s shell (or the ec2-user’s if on AWS).

Since FreeBSD separates the base operating system from third-party packages and ports, non-base shells are installed to /usr/local/bin, a location that might not be mountable at boot time with a damaged system.

It’s also possible that the system could enter a state where bash can’t run for any number of reasons (during OS upgrades, botched package updates, etc.).

If you change root’s (or AWS’s ec2-user’s) shell away from the default, you risk being unable to log in to the system with no way to correct the problem. Don’t change those user’s shells.

Set up SSH

Ensure you’re familar with and are using SSH key-based authentication.

Switch to your new user account.

# su [username]

Add your SSH public keys to ~/.ssh/authorized_keys, then lock down that file’s permissions. That file might not exist for new users, so you’ll need to create it.

$ vim ~/.ssh/authorized_keys
  (paste in your public keys)
  (save and quit)

$ chmod 600 ~/.ssh/authorized_keys

Log out as your user and return to root with:

exit

Step 3. Simplify outbound email handling

FreeBSD comes with a feature-rich inbound/outbound email system via sendmail. It’s too complex for most server installs that only need to send outbound emails (such as system alerts, cron job results, password reset emails via PHP, etc.).

We’ll replace sendmail with the lightweight, outbound-only ssmtp.

Disable sendmail

Edit /etc/rc.conf and add this to the bottom:

# Disable sendmail (we're using ssmtp)
sendmail_enable="NO"
sendmail_submit_enable="NO"
sendmail_outbound_enable="NO"
sendmail_msp_queue_enable="NO"

Then, stop the running sendmail service with:

service sendmail stop

Install ssmtp

Follow these steps to install ssmtp.

Step 4. Install a web server (Apache + PHP)

We’ll use Apache here, but there are several other web servers you could choose, including Nginx or Lighttpd.

Install Apache

Find and install the latest version of Apache.

# pkg search apache | grep -i server
apache-solr-8.11.2             High performance search server built using Lucene Java
apache24-2.4.54                Version 2.4.x of Apache web server                           <---
p5-Apache-ASP-2.63             Active Server Pages for Apache
p5-Apache-Config-Preproc-1.07  Preprocess Apache server configuration files
p5-Apache-Solr-1.07            High level interface to the Solr server
p5-Apache2-SOAP-0.73_4         Apache2 mod_perl2 SOAP Server
p5-Config-ApacheFormat-1.2_2   Parse a configuration file in the same syntax as the Apache...

# pkg install apache24

Ping your hostname and ensure that it resolves to the machine’s IP address. If you need to make changes, check either /etc/nsswitch.conf or /etc/hosts.

# ping [hostname]

Enable Apache at boot:

# sysrc apache24_enable="YES"

Configure Apache

Edit the following configuration files:

/usr/local/etc/apache24/httpd.conf

  • Make a backup copy of this file
  • Set ServerName to either a FQDN or the server’s IP address
  • Uncomment these lines:
Include etc/apache24/extra/httpd-ssl.conf
Include etc/apache24/extra/httpd-mpm.conf

# Uncomment any modules you want to enable
LoadModule authn_socache_module libexec/apache24/mod_authn_socache.so
LoadModule socache_shmcb_module libexec/apache24/mod_socache_shmcb.so
LoadModule ssl_module libexec/apache24/mod_ssl.so
LoadModule deflate_module libexec/apache24/mod_deflate.so
LoadModule expires_module libexec/apache24/mod_expires.so
LoadModule headers_module libexec/apache24/mod_headers.so
LoadModule speling_module libexec/apache24/mod_speling.so
LoadModule alias_module libexec/apache24/mod_alias.so
LoadModule rewrite_module libexec/apache24/mod_rewrite.so

/usr/local/etc/apache24/extra/httpd-ssl.conf

  • Make a backup copy of this file
  • Comment out line Listen 443
  • Remove the default SSL virtualhost example at the bottom of the file. (Hint: it starts with <VirtualHost _default_:443> and continues for about 170 lines until the closing </VirtualHost>. Remove all of those lines.)
  • Add these lines (and follow the link in your web browser, ensure the protocols listed below are still up-to-date):
# These are the recommendations as of 2023-01.
# Review in the future to make sure they're still recommended.
# https://docs.freebsd.org/en/books/handbook/network-servers/#_ssl_support

SSLProtocol all -SSLv3 -SSLv2 +TLSv1.2 +TLSv1.3
SSLProxyProtocol all -SSLv2 -SSLv3 -TLSv1 -TLSv1.1

Later, you’ll add your virtualhost files and other common configs to /usr/local/etc/apache24/Includes/.

Validate the configuration

Let’s ensure our configuration files are set up properly. Run:

# apachectl -t

Later, if Apache is running and you want to validate the configuration files, run:

# service apache24 configtest

If you get the “Could not reliably determine the server’s fully qualified domain name” error, try adding a ServerName parameter in /usr/local/etc/apache24/httpd.conf

If no errors, start Apache:

# service apache24 start

Browse to the machine’s IP address (or hostname) and make sure you see the Apache “It works!” message. If you don’t see anything, ensure that Apache is running, and monitor /var/log/httpd-error.log and /var/log/httpd-apache.log for hints that can help you identify problems.

If you’re stuck, perhaps there’s some misconfiguration somewhere. Apache’s default configuration file pathway is, in order:

  1. /usr/local/etc/apache24/httpd.conf
  2. /usr/local/etc/apache24/extra/httpd-mpm.conf
  3. /usr/local/etc/apache24/extra/proxy-html.conf
  4. /usr/local/etc/apache24/extra/httpd-ssl.conf
  5. /usr/local/etc/apache24/Include/*.conf

Add PHP support to Apache

Find the latest versions of PHP:

# pkg search php | grep -i scripting
drush-php74-8.4.11             Drupal command line and scripting interface
drush-php80-8.4.11             Drupal command line and scripting interface
drush-php81-8.4.11             Drupal command line and scripting interface
drush-php82-8.4.11             Drupal command line and scripting interface
mod_php74-7.4.32_1             PHP Scripting Language
mod_php80-8.0.25               PHP Scripting Language
mod_php81-8.1.12               PHP Scripting Language (8.1.X branch)
mod_php82-8.2.0.r2_1           PHP Scripting Language (8.2.X branch)
php74-7.4.32                   PHP Scripting Language
php80-8.0.25                   PHP Scripting Language                                       <---
php81-8.1.12                   PHP Scripting Language (8.1.X branch)                        <---
php82-8.2.0.r2                 PHP Scripting Language (8.2.X branch)                        <---

Install PHP and related packages:

# pkg install php80 mod_php80 php80-mysqli php80-pdo

# For WordPress support, also install:
# pkg install php80-{curl,dom,exif,fileinfo,filter,gd,iconv,mbstring,phar,simplexml,sodium,xml,xmlreader,zip,zlib}

Put a php.ini file into place.

FreeBSD’s PHP doesn’t come with a php.ini file. There are two templates provided, one for production machines, and one for development machines. You’ll need to select one to start with and then customize from there.

The development template has settings that reduce or eliminate OPcode caching, so code changes will be visible immediately, at the expense of page rendering speed.

# cp /usr/local/etc/php.ini-production /usr/local/etc/php.ini
-- OR --
# cp /usr/local/etc/php.ini-development /usr/local/etc/php.ini

Edit /usr/local/etc/php.ini and ensure this is set:

cgi.fix_pathinfo=0

Backup the php-fpm config file, then edit it:

# cp /usr/local/etc/php-fpm.d/www.conf{,.orig}
# vim /usr/local/etc/php-fpm.d/www.conf

  Ensure [user] and [group] are = www

Update Apache to look for and execute index.php files by editing /usr/local/etc/apache24/httpd.conf and adding index.php to DirectoryIndex, like so:

DirectoryIndex index.php index.html index.htm

Next, connect Apache with PHP by editing (or creating) /usr/local/etc/apache24/Includes/php.conf:

<FilesMatch "\.php$">
    SetHandler application/x-httpd-php
</FilesMatch>
<FilesMatch "\.phps$">
    SetHandler application/x-httpd-php-source
</FilesMatch>

Enable PHP at boot:

# sysrc php_fpm_enable=YES

Create a demo page to show that PHP is working at /usr/local/www/apache24/data/index.php and put in these contents:

<html><body>
<?php phpinfo(); ?>
</body></html>

Then, start PHP:

# service php-fpm start

Browse to your web server’s hostname or IP address and you should see a long PHP information page.

If you don’t see anything, or if you get any errors, monitor /var/log/php-fpm.log for hints that can help you identify problems. Also, check the Apache log files /var/log/httpd-error.log and /var/log/httpd-apache.log for clues.

Install a database server (MariaDB)

Find the latest version of MariaDB, then install it.

# pkg search mariadb | grep -i server

mariadb103-server-10.3.37      Multithreaded SQL database (server)
mariadb104-server-10.4.27      Multithreaded SQL database (server)
mariadb105-server-10.5.18      Multithreaded SQL database (server)
mariadb106-server-10.6.11      Multithreaded SQL database (server)          <---

# pkg install mariadb106-server

Set MariaDB to start at boot and start the service.

# sysrc mysql_enable=YES
# service mysql-server start

Secure the installation

# /usr/local/bin/mysql_secure_installation

When prompted:

  • Do not provide a root password, just press [Enter]
  • Use Unix socket authentication
  • Remove test users and databases
  • Prevent remote login
  • Flush the tables to save settings

Validate that the server is running

# mysql

You should see the MySQL prompt. Type exit to quit.

Helpful hint: Sometimes applications will complain they can’t connect to the database instance on the local machine via localhost. If that’s the case, try using 127.0.0.1, and if that fails, try whatever localhost is defined as in /usr/local/etc/mysql/my.cnf:

localhost:/var/run/mysql/mysql.sock

Final steps

After every server setup is complete, you should restart the system to make sure all systems come back online as expected.

Log in as your non-root user and try some sudo commands to ensure you can escalate permissions when needed.

Tips and tricks

Silence the login tips and tricks (fortunes)

Edit ~/.profile and comment out the line with /usr/bin/fortune.

To completely silence the rest of the login messages, add a blank file named ~/.hushlogin.

Disable the daily system email reports

The server will send you daily reports with system statuses and security reports. Instead of cluttering up your inbox, let’s send the reports to files instead.

First, create a folder to store the files:

sudo mkdir /var/log/periodic

Then, edit /etc/periodic.conf and add these lines to the bottom of the file:

# Instead of spamming yourself with daily email reports,
# output the results of these reports to a file.

# daily_output="root"
daily_output="/var/log/periodic/$(date +%Y%m%d)-daily.log"

# daily_status_security_output="root"
daily_status_security_output="/var/log/periodic/$(date +%Y%m%d)-daily-security.log"

# weekly_output="root"
weekly_output="/var/log/periodic/$(date +%Y%m%d)-weekly.log"

# weekly_status_security_output="root"
weekly_status_security_output="/var/log/periodic/$(date +%Y%m%d)-weekly-security.log"

# monthly_output="root"
monthly_output="/var/log/periodic/$(date +%Y%m%d)-monthly.log"

# monthly_status_security_output="root"
monthly_status_security_output="/var/log/periodic/$(date +%Y%m%d)-monthly-security.log"

Enable locate

locate lets you quickly find files on your machine. It’s installed by default, but unusuble until you prime the database:

/etc/periodic/weekly/310.locate

To automatically refresh the database weekly:

sudo sysrc weekly_locate_enable="YES"

Then, you can quickly find any file on your machine with:

$ locate rc.conf
/etc/defaults/rc.conf
/etc/rc.conf
/etc/rc.conf.d
/usr/share/examples/jails/rc.conf.jails
/usr/share/man/man5/rc.conf.5.gz
/usr/share/man/man5/rc.conf.local.5.gz
/usr/share/man/man5/src.conf.5.gz
/var/db/etcupdate/current/etc/defaults/rc.conf

Since the database is only updated weekly, you might want to re-prime it after installing new software or making big changes to the system. Simply run the first command above to re-prime the database and scan your filesystem for new files.