This page looks best with JavaScript enabled

Nginx, Php-FPM, MySQL, Symfony on FreeBSD

 ·  🎃 kr0m

A typical web server configuration is to have Nginx as HTTP server, MySQL as data storage, PHP as programming language, and Symfony as framework. In this occasion, I will explain everything necessary to make it work on FreeBSD.

We install mysql, nginx, and php:

pkg install mysql80-server nginx php73 php73-mysqli

We configure RC to start the services on boot:

sysrc mysql_enable="yes"
sysrc nginx_enable="yes"
sysrc php_fpm_enable="yes"

We start mysql:

service mysql-server start

We perform the initial configuration of mysql:

mysql_secure_installation

We configure FPM to listen on a Unix socket and assign the user with which the php code will be executed:

vi /usr/local/etc/php-fpm.d/www.conf

;listen = 127.0.0.1:9000
listen = /var/run/php-fpm.sock
listen.owner = www
listen.group = www
listen.mode = 0660

We copy the php test configuration file:

cp /usr/local/etc/php.ini-production /usr/local/etc/php.ini

We start FPM:

service php-fpm start

We get the number of cores:

sysctl hw.ncpu

hw.ncpu: 8

We configure the nginx workers according to the number of cores and perform a test using phpinfo():

vi /usr/local/etc/nginx/nginx.conf

user  www;
worker_processes  8;
error_log /var/log/nginx/error.log info;

events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;

    access_log /var/log/nginx/access.log;

    sendfile        on;
    keepalive_timeout  65;

    server {
        listen       80 default_server;
        root /usr/local/www/nginx;

        location / {
            try_files $uri /app.php$is_args$args;
        }

    location ~ \.php(/|$) {
        fastcgi_pass unix:/var/run/php-fpm.sock;
        fastcgi_split_path_info ^(.+\.php)(/.*)$;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        fastcgi_param DOCUMENT_ROOT $realpath_root;
    }
    location ~ \.php$ {
        return 404;
    }

    }
}

We create the log directory and files:

mkdir -p /var/log/nginx
touch /var/log/nginx/access.log
touch /var/log/nginx/error.log

The docroot is a symbolic link to a directory that could be modified by some OS update, we remove the link and create a directory in its place:

rm /usr/local/www/nginx
mkdir /usr/local/www/nginx

We start nginx:

service nginx start

We check that the php code is executed through FPM, a phpinfo will serve us for this purpose:

echo "<?php phpinfo(); ?>" > /usr/local/www/nginx/info.php

Then we delete the file:

rm /usr/local/www/nginx/info.php

We configure the nginx workers according to the number of cores and perform the vhost config as indicated by Symfony here :

vi /usr/local/etc/nginx/nginx.conf

user  www;
worker_processes  8;
error_log /var/log/nginx/error.log info;

events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;

    access_log /var/log/nginx/access.log;

    sendfile        on;
    keepalive_timeout  65;

    server {
        listen       80 default_server;
        root /usr/local/www/nginx;

        location / {
            try_files $uri /app.php$is_args$args;
        }

    location ~ ^/app\.php(/|$) {
        fastcgi_pass unix:/var/run/php-fpm.sock;
        fastcgi_split_path_info ^(.+\.php)(/.*)$;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        fastcgi_param DOCUMENT_ROOT $realpath_root;
    }
    location ~ \.php$ {
        return 404;
    }

    }
}

To deploy our project we will install Symfony and all the dependencies on which our code depends:

pkg install php73-composer php73-ctype php73-curl php73-dom php73-filter php73-hash php73-intl php73-json php73-mbstring php73-mysqli php73-openssl php73-pdo php73-pdo_mysql php73-pecl-memcached php73-phar php73-session php73-tokenizer php73-xml php73-simplexml php73-iconv

We dump the content of the web by cloning the content of the git repository or restoring a backup:

tar xvzf BACKUP
cd BACKUP

We copy the code to the nginx directory and assign the user we are using to deploy the project as the owner:

su -l
cp -r /home/kr0m/BACKUP/DOCROOT /usr/local/www/nginx/
chown -R USER:USER /usr/local/www/nginx/DOCROOT/

The user (www) with which the php code will run will need to be able to write to certain paths under var, to allow these actions ACLs are usually assigned as indicated by Symfony in its documentation , but ACLs in ZFS are not POSIX but NFS ACLs, with UFS we would not have problems with POSIX ACLs.

NOTE: For caches it is necessary to create directories with mkdir, for this we assign the append permission: p

find var -type f -exec setfacl -m u:www:rwx:allow {} ;
find var -type d -exec setfacl -m u:www:rwxp:fd:allow {} ;
find var -type f -exec setfacl -m u:USER:rwx:allow {} ;
find var -type d -exec setfacl -m u:USER:rwxp:fd:allow {} ;
exit

We adjusted the nginx docroot to the new path:

vim /usr/local/etc/nginx/nginx.conf

        root /usr/local/www/nginx/DOCROOT/web;

Restart nginx:

service nginx restart

Configure a default user and password to access the mysql CLI more comfortably:

vim .my.cnf

[client]
user=root
password=DATABASE_PASSWORD

Create the necessary users for the application to work:

root@localhost [mysql]> create user DBUSERNAME@'localhost' identified with mysql_native_password by 'XXXXXXXXXXXXXX';
root@localhost [mysql]> grant all privileges on DB.* to DBUSERNAME@'localhost';

Finally, install all the bundles our app needs and clear the symfony cache:

cd /usr/local/www/nginx/DOCROOT
composer install
php bin/console cache:clear --no-warmup -e prod

To debug, we can always start the embedded server that symfony brings, which will give us a lot of information about the errors produced:

php bin/console server:run

We can also change the php error_reporting:

vi /usr/local/etc/php.ini

error_reporting = E_ALL
display_errors = On
display_startup_errors = On
service php-fpm restart
service nginx restart

I strongly recommend filtering network traffic to allow only the services offered, as well as binding services to the loopback address whenever possible.

If you liked the article, you can treat me to a RedBull here