PHP (PHP Hypertext Preprocessor) is a source scripting language specially designed for web application development. In 2024, PHP represented a little less than 80% of the web pages generated in the world. PHP is open-source and is the core of the most famous CMS (WordPress, Drupal, Joomla!, Magento, ...).
PHP-FPM (FastCGI Process Manager) is integrated to PHP since its version 5.3.3. The FastCGI version of PHP brings additional functionalities.
CGI (Common Gateway Interface) and FastCGI allow communication between the web server (Apache, Nginx, ...) and a development language (PHP, Python, Java):
In the case of CGI, each request creates a new process, which is less efficient in performance.
FastCGI relies on a certain number of processes to treat its client requests.
PHP-FPM, in addition to better performances, brings:
The possibility of better partitioning the applications: launching processes with different uid/gid, with personalized php.ini files,
The management of the statistics,
Log management,
Dynamic management of processes and restart without service interruption ('graceful').
Note
Since Apache has a PHP module, php-fpm is more commonly used on an Nginx server.
Rocky Linux, like its upstream, offers many versions of the language. Some of them have reached the end of their life but are kept to continue hosting historical applications that are not yet compatible with new versions of PHP. Please refer to the supported versions page of the php.net website to choose a supported version.
To obtain a list of available versions, enter the following command:
First, let's see how to install and use PHP in CGI mode. We can only make it work with the Apache web server and its mod_php module. We will see, later in this document, in the FastCGI part (php-fpm) how to integrate PHP in Nginx (but also Apache).
You will be prompted to import GPG keys for the epel9 (Extra Packages for Enterprise Linux 9) and Remi repositories during installation. Enter y to import the keys:
ExtraPackagesforEnterpriseLinux9-x86_64
ImportingGPGkey0x3228467C:
Userid:"Fedora (epel9) <epel@fedoraproject.org>"
Fingerprint:FF8AD1344597106ECE813B918A3872BF3228467C
From:/etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-9
Isthisok[y/N]:y
Keyimportedsuccessfully
Remi's RPM repository for Enterprise Linux 9 - x86_64 Importing GPG key 0x478F8947:Userid : "Remi'sRPMrepository(https://rpms.remirepo.net/)<remi@remirepo.net>"Fingerprint: B1AB F71E 14C9 D748 97E1 98A8 B195 27F1 478F 8947From : /etc/pki/rpm-gpg/RPM-GPG-KEY-remi.el9Is this ok [y/N]: yKey imported successfullyRunning transaction checkTransaction check succeeded.Running transaction testTransaction test succeeded.Complete!
The default vhost should work out of the box. PHP provides a phpinfo() function that generates a summary table of its configuration. It's very useful to test the good working of PHP. However, be careful not to leave such test files on your servers. They represent a huge security risk for your infrastructure.
Create the file /var/www/html/info.php (/var/www/html being the default vhost directory of the default Apache configuration):
Using a socket when the web server and PHP server are on the same machine removes the TCP/IP layer and optimizes the performance.
When working via an interface, you have to configure listen.owner, listen.group, listen.mode to specify the owner, the owner group and the rights of the Unix socket. Warning: Both servers (web and PHP) must have access rights on the socket.
When working via a socket, you must configure listen.allowed_clients to restrict access to the PHP server to certain IP addresses.
The processes of PHP-FPM can be managed statically or dynamically.
In static mode, the number of child processes is set by the value of pm.max_children;
pm=static
pm.max_children=10
This configuration will launch 10 processes.
In dynamic mode, PHP-FPM will launch at most the number of processes specified by the value of pm.max_children, starting by launching some processes corresponding to pm.start_servers, and keeping at least the value of pm.min_spare_servers of inactive processes and at most pm.max_spare_servers inactive processes.
PHP-FPM will create a new process to replace one that has processed several requests equivalent to pm.max_requests.
By default, pm.max_requests is set to 0, meaning processes are never recycled. Using the pm.max_requests option can be interesting for applications with memory leaks.
There is a third mode of operation, the ondemand mode. This mode only starts a process when it receives a request. It is not an optimal mode for sites with strong influences and is to be reserved for specific needs (sites with very weak requests, management backend, etc.).
Note
The configuration of the operating mode of PHP-FPM is essential to ensure the optimal functioning of your web server.
The slowlog directive specifies the file that receives logging of requests that are too long (i.e., whose time exceeds the value of the request_slowlog_timeout directive).
The default location of the generated file is /var/log/php-fpm/www-slow.log.
It is essential to optimize the number of requests that will be able to be served and to analyze the memory used by the PHP scripts, to optimize the maximum amount of launched threads.
First of all, we need to know the average amount of memory used by a PHP process with the command:
whiletrue;dops--no-headers-o"rss,cmd"-Cphp-fpm|grep"pool www"|awk'{ sum+=$1 } END { printf ("%d%s\n", sum/NR/1024,"Mb") }'>>avg_php_proc;sleep60;done
After a while, this should give us a pretty accurate idea of the average memory footprint of a PHP process on this server.
The result of the rest of this document is a memory footprint of 120MB per process at full load.
On a server with 8Gb of RAM, keeping 1Gb for the system and 1Gb for the OPCache (see the rest of this document), is 6Gb left to process PHP requests from clients.
We can easily conclude that this server can accept at most 50 threads((6*1024) / 120).
A good configuration of php-fpm specific to this use case would be:
The opcache (Optimizer Plus Cache) is the first level of cache on which we can influence.
It keeps the compiled PHP scripts in memory, which strongly impacts the execution of the web pages (removes the reading of the script on disk + the compilation time).
To configure it, we must work on:
The size of the memory dedicated to the opcache according to the hit ratio, configuring it correctly
The number of PHP scripts to cache (number of keys + maximum number of scripts)
The number of strings to cache
To install it:
sudodnfinstallphp-opcache
To configure it, edit the /etc/php.d/10-opcache.ini configuration file:
opcache.memory_consumption corresponds to the amount of memory needed for the opcache (to be increased until a correct hit ratio is obtained).
opcache.interned_strings_buffer the amount of strings to cache.
opcache.max_accelerated_files is near to the result of the find ./ -iname "*.php"|wc -l command.
You can refer to an info.php page (including the phpinfo();) to configure the opcache (see for example the values of Cached scripts and Cached strings).
Note
At each new deployment of new code, it will be necessary to empty the opcache (for example by restarting the php-fpm process).
Note
Don't underestimate the speed gain that can be achieved by setting up and configuring the opcache correctly.
Author: Antoine Le Morvan
Contributors: Steven Spencer, Ganna Zhyrnova, Joseph Brinkman