How to Create a FreeBSD Web Server (FAMP Stack) - 2026 Guide

Change log

  • : The guide has been updated for 2026 using modern package versions and config defaults.
  • : Initial publication.

In this updated 2026 guide, I’ll show you how to create a FreeBSD web server using a FAMP stack (FreeBSD, Apache, MariaDB, and PHP). If you’re looking for a FreeBSD web server howto, this step-by-step tutorial walks you through the full setup.

Table of contents

  1. Before we begin
  2. Find hosting and provision an instance
  3. Set up FreeBSD
  4. Simplify outbound email handling
  5. Install the Apache web server
  6. Install PHP
  7. Install the MariaDB database server
  8. Final steps
  9. Tips and tricks

Before we begin

  • This guide is meant to supplement the FreeBSD handbook, which should be considered the canonical documentation source for FreeBSD.
  • 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 BSD-style servers. If you’re familiar with Linux, you should be able to follow along nicely!
  • This guide isn’t meant to act as a tuning guide for Apache, PHP, or MariaDB. That’s beyond the scope of this article. I may write about tuning those services in future posts!

Step 1. Find hosting and provision an instance

You can create FreeBSD instances on:

  • Vultr
  • AWS Lightsail
  • AWS EC2

FreeBSD is 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 machine to get, start small and upgrade later if you need to. A $7/mo AWS Lightsail instance (1 GB RAM, 2 vCPU, 40 GB SSD) is more than sufficient to host several WordPress sites. Static content caching and a CDN can help reduce load on the server.

Step 2. Set up FreeBSD

When the machine is provisioned it’ll come with a baseline OS image. You’ll want to do a few things first to finish the install of FreeBSD and get the machine configured for use as your web server.

Update the system

The first thing to do is to log in as root and update the system software.

Start by updating 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 doas wget git bash bash-completion tmux

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 doas (instead of sudo)

I’ve changed from recommending sudo to doas instead. doas operates the same way, yet is smaller and simpler.

/usr/local/etc/doas.conf (Ensure this file ends with a blank line)

permit nopass :wheel

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 doas 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 to other groups? Add to wheel group so user can use doas.
  4. Shell? 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 the user’s ~/.profile so that the system will parse their ~/.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 user's on AWS machines).

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. So, 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 the Apache web server

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
apache24-2.4.66                Version 2.4.x of Apache web server

# 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

First, make a backup copy of /usr/local/etc/apache24/httpd.conf, then edit the file.

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

  • 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

Make a backup copy of /usr/local/etc/apache24/extra/httpd-ssl.conf, then edit the file.

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

  • 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):
SSLProtocol -all +TLSv1.2 +TLSv1.3
SSLProxyProtocol -all +TLSv1.2 +TLSv1.3

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

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 there are 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 (all of the files below are in /usr/local/etc/apache24/):

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

Step 5. Install PHP

Find the latest versions of PHP:

# pkg search php | grep -i scripting
php82-8.2.30                   PHP Scripting Language (8.2.X branch)
php83-8.3.29                   PHP Scripting Language (8.3.X branch)
php84-8.4.16                   PHP Scripting Language (8.4.X branch)
php85-8.5.1                    PHP Scripting Language (8.5.X branch)

Helpful hint: If you're going to be running a WordPress site, the latest version of PHP might be "too new" for WordPress and third-party plugins. If you encounter errors, try installing an older version of PHP.

Install PHP and related packages (we’ll use v8.3 here):

# pkg install php83 php83-mysqli php83-pdo

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

Validate that php-fpm was installed.

php-fpm -v

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

At the end of the [PHP] section:

; (2026) This might not be necessary anymore. Leave it commented out unless necessary.
; Don't serve suggested files in misspelled URL requests.
; cgi.fix_pathinfo=0

; Dev server? Reload php files instantly
revalidate_freq=0

; Big or complex site? Divi/Elementor?
memory_limit = 256M
; memory_limit = 512M

; Need big uploads?
post_max_size = 32M
upload_max_filesize = 32M

At the end of the [Pdo_mysql] section:

[Pdo_mysql]
pdo_mysql.default_socket=/var/run/mysql/mysql.sock

Next, edit /usr/local/etc/php-fpm.d/www.conf

  • Make note of the pool name (defaults to www)
  • Make a note of the user/group (defaults to www)
  • Change listen from 127.0.0.1:9000 to /var/run/php-fpm.sock
  • Uncomment the socket permissions section (listen.owner, listen.group, and listen.mode)

Validate the configs:

php-fpm -t

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 "proxy:unix:/var/run/php-fpm.sock|fcgi://localhost/"
</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.

Step 6. Install the MariaDB database server

Find the latest version of MariaDB, then install it.

# pkg search mariadb | grep -i server

mariadb1011-server-10.11.15    Multithreaded SQL database (server)
mariadb106-server-10.6.24      Multithreaded SQL database (server)
mariadb114-server-11.4.9       Multithreaded SQL database (server)
mariadb118-server-11.8.5       Multithreaded SQL database (server)          <---

# pkg install mariadb118-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: If your application can't connect to the database, try setting the host to whatever path is in /usr/local/etc/mysql/my.cnf, like localhost:/var/run/mysql/mysql.sock.

Final steps

After every server setup is complete, restart the system to make sure all systems come back online as expected. This is a great time to make sure all services come back online at system boot.

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

Tips and tricks

Silence the login tips (fortunes)

Every time you log in to FreeBSD, it displays fun and helpful tips to familiarize you with the system. As you become proficient, you may want to turn these off.

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

To completely silence the rest of the login messages, add a blank file in your home directory 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:

doas 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:

doas 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.