A FEMP stack, which is comparable to a LEMP stack on Linux, is a collection of open-source software that is typically installed together to enable a FreeBSD server to host dynamic websites and web applications. FEMP is an acronym that stands for FreeBSD (operating system), Nginx (HTTP server pronounced Engine-x), MySQL/MariaDB (database server), and PHP (programming language to process dynamic PHP content). FreeBSD is a freely available Unix-like operating system.

This tutorial will show you how to install and deploy core components of the FEMP software stack on a FreeBSD 12.1 server using pkg, the FreeBSD package manager. You can deploy your own local server to test this guide. I can recommend DigitalOcean cloud hosting for the test server to follow along with this guide.


Before you start this guide, you'll need the following:

  • A FreeBSD 12.1 VPS.
  • A user with root privileges or sudo user to make configuration changes.
  • Basic familiarity with the FreeBSD system and command-line interface is recommended.

Before you begin

Check the FreeBSD version.

uname -ro
# FreeBSD 12.1-RELEASE-p2

Ensure that your FreeBSD system is up to date.

freebsd-update fetch install
pkg update && pkg upgrade -y

Install the necessary packages like sudo and bash which are not shipped by default on FreeBSD as part of the base system.

pkg install -y sudo bash bash-completion vim curl socat

NOTE: FreeBSD uses tcsh as root’s default and does not ship bash as part of the base system. That's why you need to install a bash package first and then change the shell to bash for user root in the next step.

Change shell to bash for user root.

chsh -s /usr/local/bin/bash root

Create a new user account with your preferred username. We use johndoe.


# Username: johndoe
# Full name: John Doe
# Uid (Leave empty for default): <Enter>
# Login group [johndoe]: <Enter>
# Login group is johndoe. Invite johndoe into other groups? []: wheel
# Login class [default]: <Enter>
# Shell (sh csh tcsh bash rbash nologin) [sh]: bash
# Home directory [/home/johndoe]: <Enter>
# Home directory permissions (Leave empty for default): <Enter>
# Use password-based authentication? [yes]: <Enter>
# Use an empty password? (yes/no) [no]: <Enter>
# Use a random password? (yes/no) [no]: <Enter>
# Enter password: your_secure_password
# Enter password again: your_secure_password
# Lock out the account after creation? [no]: <Enter>
Username   : johndoe
Password   : *****
Full Name  : John Doe
Uid        : 1001
Class      :
Groups     : johndoe wheel
Home       : /home/johndoe
Home Mode  :
Shell      : /usr/local/bin/bash
Locked     : no
# OK? (yes/no): yes
# adduser: INFO: Successfully added (johndoe) to the user database.
# Add another user? (yes/no): no
# Goodbye!

Run the visudo command and uncomment the %wheel ALL=(ALL) ALL line, to allow members of the wheel group to execute any command.


# Uncomment by removing hash (#) sign
# %wheel ALL=(ALL) ALL

Now, switch to your newly created user with su:

su - johndoe

NOTE: Replace johndoe with your username.

Set up the timezone:

sudo tzsetup

Step 1 — Installing mainline Nginx

The Nginx web server is currently one of the most popular web servers in the world. It is an excellent pick for hosting a website.

You can install Nginx using FreeBSD's package manager, pkg. A package manager allows you to install most software effortlessly from a repository maintained by FreeBSD. You can learn more about how to use pkg here.

NOTE: By default, FreeBSD will install packages from the quarterly branch. The quarterly branch does not always have the latest software, and is considered more stable than the latest branch.
You may switch to the latest branch by putting the following code into
/usr/local/etc/pkg/repos/FreeBSD.conf or /etc/pkg/FreeBSD.conf file:

FreeBSD: {
    url: "pkg+http://pkg.FreeBSD.org/${ABI}/latest"

To install the latest mainline Nginx, issue the following command:

sudo pkg install -y nginx-devel

NOTE: If you want to install stable Nginx just use nginx as package name.

Check the version:

nginx -v && nginx -V
# nginx version: nginx/1.19.1
# nginx version: nginx/1.19.1
# built with OpenSSL 1.1.1d-freebsd  10 Sep 2019
# TLS SNI support enabled
# configure arguments: --prefix=/usr/local/etc/nginx --conf-path=/usr/local/etc/nginx/nginx.conf --sbin-path=/usr/local/sbin/nginx --pid-path=/var/run/nginx.pid --error-log-path=/var/log/nginx/error.log --user=www --group=www --modules-path=/usr/local/libexec/nginx --with-http_v2_module
# . . .

Locate Nginx program file in your PATH:

which nginx
# /usr/local/sbin/nginx

This command will install the latest mainline version, which can reliably be used on a production server. If you want to install the latest stable release, just use nginx package instead of nginx-devel.

Now, enable and start Nginx:

sudo sysrc nginx_enable=yes
sudo service nginx start

To check that Nginx has started you can run the following command:

sudo service nginx status

As a result, you'll see something similar to:

# Output
nginx is running as pid 17607.

You can verify that Nginx was installed and working without errors by visiting your server's public IP address in your web browser. Navigate to your_server_IP. You will see the default "Welcome to nginx!" page.

Step 2 — Installing MySQL 8.0

Now that you have your web server up and running, it is time to install MySQL, the relational database management system. The MySQL server will organize and provide access to databases where your server can store information.

Again, you can utilize pkg to obtain and install your software.

To install MySQL 8.0 using pkg, use this command:

sudo pkg install -y mysql80-client mysql80-server

This command will install the latest version of the MySQL client and server, which is currently 8.x.x.

Check the version:

mysql --version
# mysql  Ver 8.0.20 for FreeBSD12.1 on amd64 (Source distribution)

NOTE: If you want to install MySQL version 5.7.x just use mysql57-client and mysql57-server packages.

Now, enable and start MySQL:

sudo sysrc mysql_enable=yes
sudo service mysql-server start

To check that MySQL has started you can run the following command:

sudo service mysql-server status

You'll view something similar to the following:

# Output
mysql is running as pid 19066.

As a good practice, you may run the mysql_secure_installation security script that will remove some insecure defaults and slightly limit access to your database system.

sudo mysql_secure_installation

# Securing the MySQL server deployment.
# Connecting to MySQL using a blank password.

# Would you like to setup VALIDATE PASSWORD component? N
# Please set the password for root here.
# New password: ********
# Re-enter new password: ********
# Remove anonymous users? (Press y|Y for Yes, any other key for No) : Y
# Disallow root login remotely? (Press y|Y for Yes, any other key for No) : Y
# Remove test database and access to it? (Press y|Y for Yes, any other key for No) : Y
# Reload privilege tables now? (Press y|Y for Yes, any other key for No) : Y
# Success.

# All done!

You will be asked to set a password, followed by some other questions. Enter a strong password and then for the rest of the questions press ENTER to select the defaults.

Step 3 — Installing PHP 7.4

PHP is a server-side scripting language designed for web development. PHP is an indispensable component of the FEMP stack. Also, Python or Perl are commonly used instead of PHP. However, PHP as the most popular option is used most often. Together with the database, it will give your web sites or apps dynamic behavior.

Once again leverage the pkg system to install PHP components.

To install PHP 7.4 with pkg, run this command:

sudo pkg install -y php74

Check the version.

php --version
# PHP 7.4.8 (cli) (built: Apr 21 2020 01:14:31) ( NTS )
# Copyright (c) The PHP Group
# Zend Engine v3.4.0, Copyright (c) Zend Technologies

This command will install the latest version of PHP, 7.4.

Now, enable and start PHP-FPM:

sudo sysrc php_fpm_enable=yes
sudo service php-fpm start

To check that PHP-FPM has started you can run the following command:

sudo service php-fpm status

As a result, you'll see something similar to:

# Output
php_fpm is running as pid 23005.

Installing PHP Modules (Optional)

To enhance the functionality of PHP, you can optionally install some additional modules.

To see currently compiled in PHP modules, you can run this:

php -m
# [PHP Modules]
# Core
# date
# hash
# libxml
# mysqlnd
# pcre
# Reflection
# standard

# [Zend Modules]

To search for available PHP modules, you can use this command:

pkg search ^php74-*

The results will be mostly PHP 7.4 modules that you can install:

# Output
# php74-7.4.5                    PHP Scripting Language
# php74-Ice37-3.7.2_1            Modern alternative to object middleware such as CORBA/COM/DCOM/COM+
# php74-aphpbreakdown-2.2.2      Code-Analyzer for PHP for Compatibility Check-UP
# php74-aphpunit-1.9             Testing framework for unit tests
# php74-bcmath-7.4.5             The bcmath shared extension for php
# php74-brotli-0.7.0             Brotli extension for PHP
# php74-bsdconv-11.5.0           PHP wrapper for bsdconv
# php74-bz2-7.4.5                The bz2 shared extension for php
# php74-calendar-7.4.5           The calendar shared extension for php
# php74-composer-1.10.0          Dependency Manager for PHP
# php74-ctype-7.4.5              The ctype shared extension for php
# php74-curl-7.4.5               The curl shared extension for php
# php74-dba-7.4.5                The dba shared extension for php
# php74-dddbl-2.0.1              Definition Driven Database Layer for PHP
# php74-deployer-6.4.3           Deployment tool for PHP
# php74-dom-7.4.5                The dom shared extension for php
# php74-enchant-7.4.5            The enchant shared extension for php
# php74-exif-7.4.5               The exif shared extension for php
# . . .

If, after researching, you decide that you need to install a package, you can do so by using the pkg install command. Most PHP web applications will require additional modules, so it's good to know how to search for them.

Step 4 — Configuring Nginx to Use PHP Module

Before using PHP, you must configure it to work with Nginx.

Run sudo vim /usr/local/etc/nginx/test.conf and populate the file with the below content:

# generated 2020-04-21, Mozilla Guideline v5.4, nginx 1.17.10, OpenSSL 1.1.1g, modern configuration
# https://ssl-config.mozilla.org/#server=nginx&version=1.17.10&config=modern&openssl=1.1.1g&guideline=5.4

server {
  listen 80 default_server;
  listen [::]:80 default_server;
  return 301 https://$host$request_uri;

server {
  #listen [::]:443 ssl http2;
  #listen 443 ssl http2;
  listen [::]:80;
  listen 80;
  # Replace with your IP or domain/hostname
  server_name example.com;
  root /usr/local/www/nginx;
  # RSA
  #ssl_certificate /etc/letsencrypt/example.com/fullchain.pem;
  #ssl_certificate_key /etc/letsencrypt/example.com/private.key;
  # ECC
  #ssl_certificate /etc/letsencrypt/example.com_ecc/fullchain.pem;
  #ssl_certificate_key /etc/letsencrypt/example.com_ecc/private.key;
  #ssl_session_timeout 1d;
  #ssl_session_cache shared:MozSSL:10m; # about 40000 sessions
  #ssl_session_tickets off;
  # Modern configuration
  #ssl_protocols TLSv1.3 TLSv1.2;
  #ssl_prefer_server_ciphers off;
  # HSTS (ngx_http_headers_module is required) (63072000 seconds)
  #add_header Strict-Transport-Security "max-age=63072000" always;
  # OCSP stapling
  #ssl_stapling on;
  #ssl_stapling_verify on;
  # Verify chain of trust of OCSP response using Root CA and Intermediate certs
  #ssl_trusted_certificate /path/to/root_CA_cert_plus_intermediates;
  # Replace with the IP address of your resolver
  index index.php index.html index.htm;
  location / {
    try_files $uri $uri/ =404;

  location ~ \.php$ {
    fastcgi_index index.php;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    include fastcgi_params;


Save the file and exit from vim.

Now we need to include test.conf in the main nginx.conf file. The main configuration file for Nginx lives under /usr/local/etc/nginx as nginx.conf.

Run sudo vim /usr/local/etc/nginx/nginx.conf to open the main configuration file in Vim and add the following line to the http {} block.

include test.conf;

Test Nginx configuration:

sudo nginx -t

Because you've made configuration changes in Nginx, you have to reload the service for those to be applied. Otherwise, Nginx will still work with the earlier configuration.

sudo service nginx reload

Step 5 — Testing PHP Processing

To test that your system is configured correctly for PHP, you can create a very basic PHP script. You'll call this script info.php. By default, the root is set to /usr/local/www/nginx-dist. You can create the info.php file under that location by typing:

sudo vim /usr/local/www/nginx-dist/info.php

And add this code to that file:

<?php phpinfo(); ?>

Navigate to http://your_server_IP/info.php and you will see the following page:

FEMP stack info.php

After FEMP stack installation and setup you should remove info.php file to avoid disclosing the information about the server to the public.

sudo rm /usr/local/www/nginx-dist/info.php

Step 6 — Installing acme.sh and obtaining TLS certs (optional)

Website encryption is important for two reasons:

  • Unencrypted sites dangerously expose their data and place their users at significant risk
  • Unencrypted sites generate significantly less business because Google decided to penalize unencrypted websites by ranking them lower in internet search results

This step is optional, but it's good to always think about HTTPS and securing transport layer on the web.

If you own a domain name and want to set up FEMP in production then you should definately think about TLS, otherwise, skip this section.

It is recommended to install and run acme.sh as root. Become a root user with su command.

sudo su - root

Install acme.sh:

pkg install -y acme.sh

Check acme.sh version:

acme.sh --version
# https://github.com/Neilpang/acme.sh
# v2.8.5

Locate acme.sh program file in your PATH:

which acme.sh
# /usr/local/sbin/acme.sh

Obtain RSA and ECDSA certificates for your domain:

# RSA 2048
acme.sh --issue --nginx /usr/local/etc/nginx/test.conf -d example.com -d www.example.com --ocsp-must-staple --keylength 2048 --test
acme.sh --issue --nginx /usr/local/etc/nginx/test.conf -d example.com -d www.example.com --ocsp-must-staple --keylength ec-256 --test
NOTE: Replace example.com in commands with your domain name.
NOTE: With nginx version 1.11.0+ the ssl_certificate and ssl_certificate_key directives can be specified multiple times to load certificates of different types (for example, RSA and ECDSA).

Create sensible directories to store your certs and keys in. We will use /etc/letsencrypt.

mkdir -p /etc/letsencrypt/example.com
mkdir -p /etc/letsencrypt/example.com_ecc

Install and copy certificates to /etc/letsencrypt.

acme.sh --install-cert -d example.com --cert-file /etc/letsencrypt/example.com/cert.pem --key-file /etc/letsencrypt/example.com/private.key --fullchain-file /etc/letsencrypt/example.com/fullchain.pem --reloadcmd "service nginx reload"
acme.sh --install-cert -d example.com --ecc --cert-file /etc/letsencrypt/example.com_ecc/cert.pem --key-file /etc/letsencrypt/example.com_ecc/private.key --fullchain-file /etc/letsencrypt/example.com_ecc/fullchain.pem --reloadcmd "service nginx reload"

After running the above commands, your certificates and keys will be in the following locations:

  • RSA: /etc/letsencrypt/example.com
  • ECC/ECDSA: /etc/letsencrypt/example.com_ecc

List the certs:

acme.sh --list
# Main_Domain  KeyLength  SAN_Domains  Created                       Renew
# example.com  "2048"     no           Fri Apr 17 09:27:54 UTC 2020  Tue Jun 16 09:27:54 UTC 2020
# example.com  "ec-256"   no           Fri Apr 17 09:28:23 UTC 2020  Tue Jun 16 09:28:23 UTC 2020

You can now return to the normal sudo user.



Congratulations, you've successfully installed a FEMP stack on your FreeBSD 12.1 VPS. Now you have multiple choices for what to do next. You've installed a platform that will allow you to install most kinds of websites and web software on top of it. The first application that comes to mind is Wordpress CMS. Good luck!