Nginx web server with PHP

Introduction

Nginx is a powerful, lightweight and highly scalable web server. Nginx is known for its efficiency and speed. Nginx is configured using simple, well-structured configuration files. The syntax is clear and easy to understand, which facilitates administration and customisation. FreeBSD provides an excellent basis for operating an Nginx web server.

Goals

Together with PHP, Nginx forms the web server and thus the basis for many articles based on it.

NEW: For the very impatient I have a console only section. There are only commands, no explanations.

Last update:

  • 28.08.2024: Minor fixes and PHP 8.4 tested
  • 10.03.2024: HTTPS and self signed certificates added
  • 03.03.2024: Initial document

Requirements

  • TrueNAS Core or pure FreeBSD server with iocage installed

Diagramm

The setup, including all optional possibilities, looks like this:

                   ┌────────────────────────────────────────────┐
                   │  TrueNAS            Optional:              │
                   │ ┌────────────────┐ ┌─────────────────────┐ │
                   │ │ jails/JAILNAME │ │ jails_data/JAILNAME │ │
                   │ │   php   ──┐    │ │                     │ │
LAN: 0.0.0.0:80  ──┼─┼─► nginx ──┴────┼─┼─► conf              │ │
                   │ └────────────────┘ └─────────────────────┘ │
                   └────────────────────────────────────────────┘

Create jail

A separate jail is required if further web applications are to be built on Nginx/PHP.
Here we use web as the jail name.

Optional: Data directories

This is more for advanced users who already have some experience.

How certain data directories are stored outside the jail is explained here.
The following directories are required:

└── /mnt/tank/jails_data
    └── JAILNAME # Name of the jail in which the web server and PHP are to be installed
        └── conf # Storage for configuration files (in the jail: /mnt/conf)

Set up jail

Login to the jail via SSH: ssh USERNAME@IP or ssh USERNAME@HOSTNAME to gain root rights with su.

Customise package source

Package sources should be customised, see separate article.

Install packages & activate services

Now update the package source with pkg update and then install the required packages: pkg install -y nginx php84 php84-extensions.

Create configuration

The configuration is divided into different files for a better overview:
PHP-FPM and other applications are placed in a separate directory: mkdir /usr/local/etc/nginx/conf.d

  • /usr/local/etc/nginx/nginx.conf = General configuration
  • /usr/local/etc/nginx/conf.d/ = Folder for further configuration files
  • /usr/local/etc/nginx/conf.d/php.conf = Enable PHP in NGINX

OPTIONAL: Set symlinks to refer to the configuration in /mnt/conf/ if necessary: ln -sf /mnt/conf/nginx.conf /usr/local/etc/nginx/nginx.conf
ln -sf /mnt/conf/php.conf /usr/local/etc/nginx/conf.d/php.conf
ln -sf /mnt/conf/dhparam.pem /usr/local/etc/nginx/dhparam.pem
ln -sf /mnt/conf/nginx.key /usr/local/etc/nginx/nginx.key
ln -sf /mnt/conf/nginx.crt /usr/local/etc/nginx/nginx.crt
ln -sf /mnt/conf/www.conf /usr/local/etc/php-fpm.d/www.conf

Zertifikat erstellen (selbstsigniert)

Dienste sollten durch eine abgesicherte Verbindung aufgerufen werden, auch lokal.

Mit dem folgenden Befehl wird ein selbstsigniertes Zertifikat erzeugt. Das wird zwar im Browser (einmalig) eine Warnung erzeugen, sorgt aber für eine verschlüsselte Verbindung.

  • Diffie-Hellman parameter erstellen: openssl dhparam -out /usr/local/etc/nginx/dhparam.pem 4096
  • Zertifikat erstellen: openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -sha256 -days 3650 -nodes -keyout /usr/local/etc/nginx/nginx.key -out /usr/local/etc/nginx/nginx.crt -subj "/C=DE/ST=NRW/L=ERKRATH/O=BSDBOX/OU=IT/CN=rss.bsdbox.local"

Das Zertifikat wird mit folgenden Parametern erstellt, gerne anpassen:

C = Country Name (2 letter code) [AU]: DE
ST = State or Province Name (full name) [Some-State]: NRW
L = Locality Name (eg, city) []: ERKRATH
O = Organization Name (eg, company) [Internet Widgits Pty Ltd]: BSDBOX
OU = Organizational Unit Name (eg, section) []: IT
CN = Common Name (e.g. server FQDN or YOUR name) []: rs.bsdbox.local
cat > /usr/local/etc/nginx/nginx.conf << 'EOF'
worker_processes auto;
error_log /var/log/nginx-error.log;

events {
 worker_connections 1024;
 use kqueue;
 multi_accept on;
}

http {
 access_log /var/log/nginx/access.log;
 include mime.types;
 default_type application/octet-stream;

 sendfile on;
 tcp_nopush on;
 tcp_nodelay on;
 reset_timedout_connection on;
 keepalive_timeout 65;
 keepalive_requests 1000;
 types_hash_max_size 2048;
 send_timeout 30;
 server_names_hash_max_size 4096;

 gzip on;
 gzip_disable "msie6";
 gzip_vary on;
 gzip_proxied any;
 gzip_comp_level 6;
 gzip_buffers 16 8k;
 gzip_http_version 1.1;
 gzip_types application/javascript application/rss+xml application/vnd.ms-fontobject application/x-font application/x-font-opentype application/x-font-otf application/x-font-truetype application/x-font-ttf application/x-javascript application/xhtml+xml application/xml font/opentype font/otf font/ttf image/svg+xml image/x-icon text/css text/javascript text/plain text/xml;

 client_header_timeout 180s;
 client_body_temp_path /var/tmp/nginx/client_body_temp;

 ssl_certificate /usr/local/etc/nginx/nginx.crt; # RSA Cert
 ssl_certificate_key /usr/local/etc/nginx/nginx.key; # RSA Key
 ssl_dhparam /usr/local/etc/nginx/dhparam.pem; # 4096 Diffie-Hellman parameter

 proxy_buffer_size 4k;
 proxy_buffers 8 16k;
 proxy_busy_buffers_size 64k;
 proxy_temp_file_write_size 64k;
 proxy_temp_path /var/tmp/nginx/proxy_temp;
 proxy_cache_valid 1m;

 include /usr/local/etc/nginx/conf.d/*.conf;
}
'EOF'
cat > /usr/local/etc/nginx/conf.d/php.conf << 'EOF'
upstream php-handler {
 server unix:/var/run/php-fpm.sock;
}
'EOF'
cat > /usr/local/etc/php-fpm.d/www.conf << 'EOF'
[www]
user = www
group = www
listen = /var/run/php-fpm.sock
listen.owner = www
listen.group = www
listen.mode = 0660
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
env[HOSTNAME] = $HOSTNAME
env[PATH] = /usr/local/bin:/usr/bin:/bin
env[TMP] = /tmp
env[TMPDIR] = /tmp
env[TEMP] = /tmp
'EOF'

Customise configuration

PHP is a widely used scripting language that is integrated into a web server as a module.
The first step is to activate the basic configuration supplied: cp /usr/local/etc/php.ini-production /usr/local/etc/php.ini

OPTIONAL: Set symlinks to refer to the configuration in /mnt/conf/ if necessary:
ln -sf /mnt/conf/99-custom.ini /usr/local/etc/php/99-custom.ini

There are some options in /usr/local/etc/php.ini that need to be customised. The clearest way to do this is with a separate ini file.

cat > /usr/local/etc/php/99-custom.ini << 'EOF'
date.timezone = Europe/Berlin
post_max_size=16M
upload_max_filesize=16M
max_execution_time=300
max_input_time = 300
memory_limit=512M
display_errors=Off
expose_php=Off
register_globals=Off
file_uploads=On
allow_url_fopen=Off
disable_functions=show_source, system, shell_exec, proc_open, proc_nice
cgi.fix_pathinfo=0
'EOF'

Start services

Activate services and start them automatically when starting the jail: service nginx enable && service php_fpm enable. We have now reached the end of the preparations and all services can now be started with service php_fpm start && service nginx start.

Important. There is no Server section yet. This means that the web server is running but is not delivering any pages. So don't be surprised.

Backup

Die Konfiguration selber ist statisch und muss eigentlich nicht separat und vor allem nicht regelmäßig gesichert werden. Dieses Jail ist auch von Grund auf sehr schnell wieder aufgebaut. Vor allem dann nicht, wenn die Daten außerhalb des Jails liegen. Sollte dennoch das Bedürfnis nach einem Backup aufkommen, so lässt sich mit diesem Befehl schnell erledigen:

Manuell

tar -cpzhf /mnt/backup/conf_`date +%Y%m%d`.tar.gz /usr/local/etc/nginx /usr/local/etc/php-fpm.d /usr/local/etc/php

Automatisch

Hier in dem Beispiel werden die Verzeichnisse /usr/local/etc/nginx /usr/local/etc/php-fpm.d /usr/local/etc/php per Cron Job jeden Sonntag um 22 Uhr gesichert.
Das Entfernen von alten Backups wird mit einer Aufgabe für die Applikation später mit erledigt (z.B. bei FresRSS)

echo "# Config Backup" >> /etc/crontab
echo "0 22 * * 7 root "tar -cpzhf /mnt/backup/conf_'$(date +\%Y\%m\%d)'.tar.gz /usr/local/etc/nginx /usr/local/etc/php-fpm.d /usr/local/etc/php"" >> /etc/crontab

Console

OPTIONAL = Nur durchführen wenn die Konfiguration außerhalb des Jails liegt
INITIAL = Nur bei der Erstinstallation ausführen oder wenn die Konfiguration nicht außerhalb des Jails liegt

  • Jail erstellen
  • OPTIONAL Datenverzeichnisse außerhalb des Jails verbinden
  • Paketquellen anpassen
  • Nginx und PHP ist installiert
    pkg install -y nginx php84 php84-extensions
    mkdir /usr/local/etc/nginx/conf.d
    OPTIONAL: ln -sf /mnt/conf/nginx.conf /usr/local/etc/nginx/nginx.conf
    OPTIONAL: ln -sf /mnt/conf/php.conf /usr/local/etc/nginx/conf.d/php.conf
    OPTIONAL: ln -sf /mnt/conf/dhparam.pem /usr/local/etc/nginx/dhparam.pem
    OPTIONAL: ln -sf /mnt/conf/nginx.key /usr/local/etc/nginx/nginx.key
    OPTIONAL: ln -sf /mnt/conf/nginx.crt /usr/local/etc/nginx/nginx.crt
    OPTIONAL: ln -sf /mnt/conf/www.conf /usr/local/etc/php-fpm.d/www.conf
    OPTIONAL: ln -sf /mnt/conf/99-custom.ini /usr/local/etc/php/99-custom.ini
    INITIAL: fetch https://raw.githubusercontent.com/marzlberger/bsdbox/main/ngingx-php/nginx.conf -o /usr/local/etc/nginx/nginx.conf
    INITIAL: fetch https://raw.githubusercontent.com/marzlberger/bsdbox/main/ngingx-php/99-custom.ini -o /usr/local/etc/php/99-custom.ini
    INITIAL: fetch https://raw.githubusercontent.com/marzlberger/bsdbox/main/ngingx-php/php.conf -o /usr/local/etc/nginx/conf.d/php.conf
    INITIAL: fetch https://raw.githubusercontent.com/marzlberger/bsdbox/main/ngingx-php/www.conf -o /usr/local/etc/php-fpm.d/www.conf
    openssl dhparam -out /usr/local/etc/nginx/dhparam.pem 4096
    openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -sha256 -days 3650 -nodes -keyout /usr/local/etc/nginx/nginx.key -out /usr/local/etc/nginx/nginx.crt -subj "/C=DE/ST=NRW/L=ERKRATH/O=BSDBOX/OU=IT/CN=rss.bsdbox.local"
    cp /usr/local/etc/php.ini-production /usr/local/etc/php.ini
    service nginx enable && service php_fpm enable
    service php_fpm start && service nginx start

Voilá