Introduction
PHP-FPM Pools
PHP-FPM’s architecture involves a parent process, running as root, that spins up “pools” of child processes. To illustrate, here is ps output with 2 pools:
# ps auxf | grep [f]pm
root 2404 0.0 0.8 232464 8568 ? Ss 11:28 0:00 php-fpm: master process (/etc/php-fpm.conf)
site1 2405 0.0 0.3 232440 3876 ? S 11:28 0:00 \_ php-fpm: pool site1
site2 2406 0.0 0.3 232440 3876 ? S 11:28 0:00 \_ php-fpm: pool site2
Note that each pool runs under a different system user. In fact, when configuring pools, we have full control over many important items, including:
- The system user and group the pool runs under.
- Limits on how many child processes each pool can spin up.
- PHP configuration settings (we can set php_flag and php_value items independently for each pool).
Installation and Setup
Starting with PHP 7.0, IUS offers a meta-package called phpXX-fpm-nginx that provides a quick working setup of PHP-FPM and Nginx. Just yum install php74-fpm-nginx start and enable the php-fpm and nginx services, and you’ll be up and running. EL 8 sets up essentially the same situation when you fpm dnf install php-fpm nginx . Ubuntu and Debian do a similar thing when you apt install nginx php . In all cases, you get a default, non-optimized setup that doesn’t incorporate our best practices. As discussed below, you should still work through the following steps for a manual install to make sure Nginx and PHP-FPM are configured sensibly and efficiently.
Step 1 – Install Packages
8
The following instructions are a bit generic; of course you’ll want to choose the right PHP version via IUS on EL 7, enable the right PHP module on EL8 , etc.
# EL 7
yum install nginx php-fpm
# EL 7 / IUS
yum install nginx phpXX-fpm phpXX-fpm-nginx
# EL 8/9
dnf install nginx php-fpm
# Ubuntu 18/20/22, Debian 10/11
apt update ; apt install nginx php-fpm
Step 2 – Configure PHP-FPM
Decide what Pools You Need
Consider what approach to pools this setup needs:
- One pool per VirtualHost? This is our normal approach; don’t deviate from it without strong reasons.
- Default pool? On all package installs, you’ll get a pool named “www” that runs under the apache/www-data user, and which can be used as a default pool. If you configure other pools (as is our normal practice) and explicitly connect all virtual hosts to these other pools, then you won’t need to use the default pool at all. But the below instructions will configure it efficiently and leave it in place to be used by any sites that are not explicitly connected to another pool.
- Multiple pools for some sites? While rare, some sites benefit from multiple PHP-FPM pools. We sometimes do this for Magento, configuring one pool for admin requests, and another for customer requests. A similar situation can occur for sites where you have both a blog and a store or a blog and a forum, etc. The motivation is that you can configure different resource limits and PHP settings for these various pools, in keeping with the needs of the different applications or user tasks.
Configure the Default Pool
First find and cd to the directory where PHP-FPM pools are configured:
OS PHP-FPM Pool Directory
EL 7/8 cd /etc/php-fpm.d
Ubuntu/Debian cd /etc/php/?.?/fpm/pool.d
After installing PHP-FPM, this directory will have a single file, www.conf . This is a template for your “default” pool. It is a long file, with copious comments and lots of default settings.
- Copy this file to www.conf.original. This preserves the packaged file so we can easily refer to its comments.
- Create a simple www.conf. Ideally, this will be configured so that it uses no resources unless virtual hosts use it. To accomplish this, use the process manager with a small own pool explicitly defined.
Configure Custom Pools
There are three process managers available for PHP-FPM pools. The two that make most sense in our setups are dynamic and ondemand
Note when using pm = dynamic
PHP-FPM child processes allocate more memory as needed, but never free memory. So child processes that have been running a long time can become very fat; they will hold onto as much memory as was needed to service their most memory-intensive request. Two of these settings can have a huge impact on memory usage by making sure child processes are regularly recycled and don’t live too long:
- pm.max_requests
- pm.max_spare_servers
Package default values are very inefficient for these two settings, and often lead to memory exhaustion tickets. So be sure to set these values manually
Here are sensible example settings for each process manager (of course you will want to customize these further):
pm = dynamic
[busysite]
listen = /run/php-fpm/$pool.sock
listen.owner = $pool
listen.group = www-data ; Set to the user the webserver runs as: apache/nginx/www-data
listen.mode = 0660
user = $pool
group = $pool
pm = dynamic
pm.max_children = 25
pm.start_servers = 2
pm.min_spare_servers = 2
pm.max_spare_servers = 10
pm.max_requests = 500
php_admin_value[error_log] = /var/log/php-fpm-pools/$pool/error.log
pm = ondemand
[smallsite]
listen = /run/php-fpm/$pool.sock
listen.owner = $pool
listen.group = www-data ; Set to the user the webserver runs as: apache/nginx/www-data
listen.mode = 0660
user = $pool
group = $pool
pm = ondemand
pm.max_children = 10
pm.max_requests = 20
pm.process_idle_timeout = 10s
php_admin_value[error_log] = /var/log/php-fpm-pools/$pool/error.log
Note two things about the pool name (which is on the first line, in square brackets):
- Each pool needs a unique name.
- You can reference the name later in the config via . So it’s convenient to use the username or domain name as pool name. $pool We strongly encourage that each PHP-FPM pool run with a unique user account and the user account be used for the pool name
Configure Logging Directories
If you look at the above sample pool configurations you’ll notice this line:
- php_admin_value[error_log] = /var/log/php-fpm-pools/$pool/error.log
Remember that PHP-FPM pools run PHP under a custom user. So we’ll need to write their error logs in a directory where the custom user has write permission. This rules out directories like , , and , because these directories are owned by system users /var/log/php-fpm/var/log/httpd /var/log/nginx like root and php-fpm. Even if you change directory ownership and permissions, they will be reset on package updates. One common solution is to just make a set of directories for these logs. This involves two steps:
1 .Create the pool directories. If your pool names are usernames, then you can do something like:
for u in user1 user2 user3 ; do mkdir -p /var/log/php-fpm-pools/$u ; chown $u: /var/log/php-fpm-pools
/$u ; done
2. Add logs from php-fpm-pools to the PHP-FPM log rotation config.
/etc/logrotate.d/php-fpm
/var/log/php-fpm-pools/*/*log
# In Ubuntu and Debian this next line will look like “/var/log/php?.?-fpm/*log {”
/var/log/php-fpm/*log {
missingok
notifempty
sharedscripts
delaycompress
postrotate
/bin/kill -SIGUSR1 `cat /run/php-fpm/php-fpm.pid 2>/dev/null` 2>/dev/null || true
endscript
}
Start and Enable PHP-FPM
After starting PHP-FPM, you should see a socket file for each pool you’ve created:
# EL with two sites
systemctl enable php-fpm –now
ls -lh /run/php-fpm
total 4.0K-rw-r–r–. 1 root root 4 Feb 8 15:29 php-fpm.pid
srw-rw—-. 1 site1 apache 0 Feb 8 15:29 site1.sock
srw-rw—-. 1 site2 apache 0 Feb 8 15:29 site2.sock
# Ubuntu 20.04 with default site only
systemctl enable php7.4-fpm –now
ls -lh /run/php
total 4.0K
srw-rw—- 1 www-data www-data 0 Feb 8 15:37 php-fpm.sock-rw-r–r– 1 root root 6 Feb 8 15:37 php7.4-fpm.pid
Step 3 – Configure Nginx
Config Needed for Default Pool
The default PHP-FPM pool needs some configuration outside of any will cause any virtual host that lacks explicit configuration for connecting to PHP-FPM to use the default pool. Newer OS’es are all setting this up by default. Here are OS-specific instructions:
If PHP 7.0 or later has been installed via IUS, then make sure to install the php7x-fpm-httpd package. This creates configuration files at/etc/nginx/conf.d/php-fpm.conf and /etc/nginx/default.d/php.conf which connect Nginx to the default PHP-FPM pool. Alternatively, this configuration file can be created manually; here are the relevant parts of its contents:
/etc/nginx/conf.d/php-fpm.conf
# PHP-FPM FastCGI server
# network or unix domain socket configuration
upstream php-fpm {
#server 127.0.0.1:9000;
server unix:/run/php-fpm/www.sock;
}
If you installed the php7x-fpm-httpd package via IUS, the upstream statement leaves the first line uncommented so that a TCP socket is used. Since we are using a Unix socket, make sure to edit this file as shown above.
/etc/nginx/default.d/php.conf
# pass the PHP scripts to FastCGI server
#
# See conf.d/php-fpm.conf for socket configuration
#
index index.php index.html index.htm;
location ~ \.(php|phar)(/.*)?$ {
fastcgi_split_path_info ^(.+\.(?:php|phar))(/.*)$;
fastcgi_intercept_errors on;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_pass php-fpm;
}
The php-fpm package install creates an Nginx configuration file at /etc/nginx/conf.d/php-fpm.conf . This connects Nginx to the default pool; nothing
further is needed.
The php-fpm package install creates an Nginx configuration file at /etc/nginx/snippets/fastcgi-php.conf The default virtual host has a reference to this snippet which is commented out. Uncomment the relevant lines:
/etc/nginx/sites-available/default
# pass PHP scripts to FastCGI server
location ~ \.php$ {
include snippets/fastcgi-php.conf;
# With php-fpm (or other unix sockets):
fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
# With php-cgi (or other tcp sockets):
# fastcgi_pass 127.0.0.1:9000;
}
Upstream Definitions in HTTP Context
We will define upstream servers for all PHP-FPM pools within the http context. The upstream directive is very powerful; for example, in a multi-tier setup you can list all the servers in your PHP-FPM tier, and Nginx will send requests to them in a round-robin manner. In our normal case, however, we will just list a single Unix socket within each upstream:
upstream php-fpm-site1 {
server unix:/var/run/site1.sock;
}
upstream php-fpm-site2 {
server unix:/var/run/site2.sock;
}
Location Definitions in Server Contexts
Within each server context, nested inside the http context, we will set up a location context to handle PHP-FPM proxy information.
A location context simply sets some configuration based on the request URI.
Example Location Context
.
Example Location Context
context to handle PHP-FPM proxy information.
http {
# Other config…
server {
# Other config…
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass php-fpm-site1;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
}
Variable Explanations
Start and Enable Nginx
Just a few little things left:
# EL 7/8/9 – Need to start and enable Nginx
systemctl enable nginx –now
# Ubuntu and Debian – Need to reload Nginx
nginx -t
systemctl reload nginx