PHP-FPM status page

You can enable a php-fpm status page that will provide details about php-fpm's health.  This can also be useful for scripts to check the status and monitor for issues.  Setup is very easy and can be configured by uncommenting (or adding) an option in the php-fpm pool configuration file and ensuring the web server allows the traffic. 

The default php-fpm pool configuration file provided on package installation can be found at the following locations.  Keep in mind if you are running an Ubuntu based system that the path could be different depending on the version of php you are running.  Just substitute the version number for what you are running.

# CentOS default pool
/etc/php-fpm.d/www.conf
# Ubuntu default pool
/etc/php/7.0/fpm/pool.d/www.conf

In the default configuration, we want to uncomment the "pm.status_path" value.  (See the bold text below)  I have included the default comments as it provides great information on what the status page can do.

; The URI to view the FPM status page. If this value is not set, no URI will be
; recognized as a status page. It shows the following informations:
; pool - the name of the pool;
; process manager - static, dynamic or ondemand;
; start time - the date and time FPM has started;
; start since - number of seconds since FPM has started;
; accepted conn - the number of request accepted by the pool;
; listen queue - the number of request in the queue of pending
; connections (see backlog in listen(2));
; max listen queue - the maximum number of requests in the queue
; of pending connections since FPM has started;
; listen queue len - the size of the socket queue of pending connections;
; idle processes - the number of idle processes;
; active processes - the number of active processes;
; total processes - the number of idle + active processes;
; max active processes - the maximum number of active processes since FPM
; has started;
; max children reached - number of times, the process limit has been reached,
; when pm tries to start more children (works only for
; pm 'dynamic' and 'ondemand');
; Value are updated in real time.
; Example output:
; pool: www
; process manager: static
; start time: 01/Jul/2011:17:53:49 +0200
; start since: 62636
; accepted conn: 190460
; listen queue: 0
; max listen queue: 1
; listen queue len: 42
; idle processes: 4
; active processes: 11
; total processes: 15
; max active processes: 12
; max children reached: 0
;
; By default the status page output is formatted as text/plain. Passing either
; 'html', 'xml' or 'json' in the query string will return the corresponding
; output syntax. Example:
; http://www.foo.bar/status
; http://www.foo.bar/status?json
; http://www.foo.bar/status?html
; http://www.foo.bar/status?xml
;
; By default the status page only outputs short status. Passing 'full' in the
; query string will also return status for each pool process.
; Example:
; http://www.foo.bar/status?full
; http://www.foo.bar/status?json&full
; http://www.foo.bar/status?html&full
; http://www.foo.bar/status?xml&full
; The Full status returns for each process:
; pid - the PID of the process;
; state - the state of the process (Idle, Running, ...);
; start time - the date and time the process has started;
; start since - the number of seconds since the process has started;
; requests - the number of requests the process has served;
; request duration - the duration in µs of the requests;
; request method - the request method (GET, POST, ...);
; request URI - the request URI with the query string;
; content length - the content length of the request (only with POST);
; user - the user (PHP_AUTH_USER) (or '-' if not set);
; script - the main script called (or '-' if not set);
; last request cpu - the %cpu the last request consumed
; it's always 0 if the process is not in Idle state
; because CPU calculation is done when the request
; processing has terminated;
; last request memory - the max amount of memory the last request consumed
; it's always 0 if the process is not in Idle state
; because memory calculation is done when the request
; processing has terminated;
; If the process is in Idle state, then informations are related to the
; last request the process has served. Otherwise informations are related to
; the current request being served.
; Example output:
; ************************
; pid: 31330
; state: Running
; start time: 01/Jul/2011:17:53:49 +0200
; start since: 63087
; requests: 12808
; request duration: 1250261
; request method: GET
; request URI: /test_mem.php?N=10000
; content length: 0
; user: -
; script: /home/fat/web/docs/php/test_mem.php
; last request cpu: 0.00
; last request memory: 0
;
; Note: There is a real-time FPM status monitoring sample web page available
; It's available in: @EXPANDED_DATADIR@/fpm/status.html
;
; Note: The value must start with a leading slash (/). The value can be
; anything, but it may not be a good idea to use the .php extension or it
; may conflict with a real PHP file.
; Default Value: not set
; Uncomment the following to allow FPM status
pm.status_path = /status

Once you have uncommented the pm.status_path, lets validate the syntax is fine before we reload the service

# CentOS
[root@server /]# php-fpm -t
[09-Jun-2018 15:43:35] NOTICE: configuration file /etc/php-fpm.conf test is successful
# Ubuntu
root@server~# php-fpm7.0 -t
[09-Jun-2018 19:19:58] NOTICE: configuration file /etc/php/7.0/fpm/php-fpm.conf test is successful

Now reload the php-fpm service to make the change active

# CentOS 5,6
[root@server /]# service php-fpm reload
# CentOS 7
[root@server /]# systemctl reload php-fpm
# Ubuntu
root@server:~# systemctl reload php7.0-fpm.service

The status page is now available to the default php-fpm pool.  If you have multiple php-fpm pools, you may need to add the "pm.status_path = /status" directive to the pool you want the status of.

Now, that php-fpm is configured to display its status, we need to configure the web server to allow the request.


Nginx

In the Nginx server block for your website, you need to allow the request.  We can use the server block to limit traffic to only trusted IP address since the information provided should be considered sensitive.

# Enable php-fpm status page
location ~ ^/(status|ping)$ {
## disable access logging for request if you prefer
access_log off;

## Only allow trusted IPs for security, deny everyone else
# allow 127.0.0.1;
# allow 1.2.3.4; # your IP here
# deny all;

fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_index index.php;
    include fastcgi_params;
## Now the port or socket of the php-fpm pool we want the status of
    fastcgi_pass 127.0.0.1:9000;
    # fastcgi_pass unix:/run/php-fpm/your_socket.sock;
}

Just make sure that the fastcgi_pass directive points to the port or socket that your application is talking to.  If your not sure what to use, check your Nginx configuration file your editing for the fastcgi_pass directive in your php location block.

With the location block added, check the syntax and reload Nginx

# Check the Syntax
[root@server ]# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

# sysvinit
[root@server /]# service nginx reload
# Systemd
[root@server /]# systemctl reload nginx

Verify

Now that we have configured php-fpm and the web server to allow the /status request, you can view the status in a browser via http://[IP or Domain]/status?full or via the curl command.

Review the comments in the configuration that I provided above for all the options you can pass to the status page.  You can get a simple overview with just /status or more information with /status?full

You can also get the output in different formats such as json or html to integrate into your monitoring or automation systems.

The following is example output of the php-fpm status.

[root@server ]# curl -L 127.0.0.1/status
pool:                 www
process manager:      dynamic
start time:           09/Jun/2018:16:53:49 -0400
start since:          2284
accepted conn:        220
listen queue:         0
max listen queue:     0
listen queue len:     128
idle processes:       5
active processes:     1
total processes:      6
max active processes: 2
max children reached: 0
slow requests:        0

[root@server]# curl -L 127.0.0.1/status?full
pool:                 www
process manager:      dynamic
start time:           09/Jun/2018:16:53:49 -0400
start since:          3339
accepted conn:        365
listen queue:         0
max listen queue:     0
listen queue len:     128
idle processes:       9
active processes:     1
total processes:      10
max active processes: 5
max children reached: 0
slow requests:        0

************************
pid:                  10300
state:                Idle
start time:           09/Jun/2018:16:53:49 -0400
start since:          3339
requests:             56
request duration:     99522
request method:       GET
request URI:          /framework/main.php?url=/&
content length:       0
user:                 -
script:               /var/www/vhosts/website.com/html/framework/main.php
last request cpu:     100.48
last request memory:  14680064

************************
pid:                  10301
state:                Idle
start time:           09/Jun/2018:16:53:49 -0400
start since:          3339
requests:             56
request duration:     102533
request method:       GET
request URI:          /framework/main.php?url=/&
content length:       0
user:                 -
script:               /var/www/vhosts/website.com/html/framework/main.php
last request cpu:     78.02
last request memory:  14680064 ... [output truncated] ...

We can see that the php-fpm status page is now working.  You can use this information for your monitoring systems or automation systems to take action based on the status of the php-fpm pools.


Practical Monitoring

So, now that we are monitoring our php-fpm processes, what should we look for?  How do we know we have a problem?

Lets focus on the holistic view and only pass the /status.  We can review the items in bold to know when a problem has happened, or is about to happen

[root@server ]# curl -L 127.0.0.1/status
pool:                 www
process manager:      dynamic
start time:           09/Jun/2018:16:53:49 -0400
start since:          3727
accepted conn:        412
listen queue:         0
max listen queue:     0
listen queue len:     128
idle processes:       9
active processes:     1
total processes:      10
max active processes: 5
max children reached: 0
slow requests:        0
max children reached

For basic monitoring, you should keep an eye on "max children reached".  The "max children reached" value will increment whenever the process limit has been reached and php-fpm is unable to create more children to handle requests.  (This value only matters when php-fpm is operating in dynamic and/or ondemand mode, with dynamic being the default mode.)

If "max children reached" is anything above 0, that means you had requests to php-fpm queuing and the user had to wait for a process to be free to be handeled.  This could indicate a traffic spike that is overloading your server, or perhaps a database issue that is slowing the response of requests and impacting users.  Moral of this value, is you want to know when it is anything above 0.

You could create a URL monitor that checks for that value and triggers an alert or email if the value is above 0.  If this alert triggers, you know you are currently encountering a problem.

idle processes

What if you want to know before a problem happens?  This requires you know a little more about your php-fpm configuration to get right.  Php-fpm can be run in multiple different modes: static, dynamic, or ondemand.  Dynamic is the default so lets talk about what this value means when you are set to dynamic.

In your php-fpm configuration you have a number of values set to control the number of processes that are allowed to spawn and handle requests

pm.max_children - the maximum number of children that can be alive at the same time.
pm.start_servers - the number of children created on startup.
pm.min_spare_servers - the minimum number of children in 'idle' state (waiting to process). If the number of 'idle' processes is less than this number then some children will be created.
pm.max_spare_servers - the maximum number of children in 'idle' state (waiting to process). If the number of 'idle' processes is greater than this number then some children will be killed.

Based on those values that we have set, we could trigger an action based on the number of idle processes available.  So lets say for our example we have the following php-fpm pool configuration

pm.max_children = 20
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 10

We can see that at maximum, we may have 20 php-fpm processes (max_children).  When the pool is created it will have 5 idle processes (start_servers), we want to maintain at least 5 idle processes (min_spare_servers), and we don't want more than 10 idle processes (max_spare_servers).

Because php-fpm is running in dynamic mode, it will create more processes as needed and tear them down when they are not needed.  Since we have a maximum of 20 processes and a minimum of 5 idle processes, we know we are approaching capacity when we get lower than 5 idle processes. 

If we want to know before a problem is about to happen, we could monitor the "idle processes" in the status and trigger a warning when there are 2 or less processes idle.  Since we know php-fpm wants 5 idle processes, the only reason it would not create more processes is if it is approaching the max_children value.

If you have automation in place, when '"idle processes" is low, it would be a good time to spin up more web servers or add additional capacity.   You could use this same value to scale back down automatically.

If you have a dedicated web server running just Apache/Nginx and php-fpm, you may want to configure php-fpm to run in a static mode.  There are some performance gains in static mode as the overhead in creating and destroying processes is mostly removed.  Your 'idle processes' trigger is pretty easy to calculate in static mode.  If your running php-fpm in static mode and have 50 children processes, you know your close to capacity once you have less than 5 processes idle.

listen queue

The listen queue is another value you can monitor to find problems before they happen.  The listen queue value is the number of requests pending connections to the pool.  I mention this value after we talked about 'idle processes' because once this value is hit, your processes are already full.  On a website with low to medium traffic, monitoring the idle processes may be ideal, however for larger sites, its not uncommon for all the processes to be in use and there be a backlog of connections waiting to be processed by php-fpm.  The total number of connections that can be backlogged is configured in the pool via the listen.backlog directive, which defaults to 511

; Set listen(2) backlog.
; Default Value: 511 (-1 on FreeBSD and OpenBSD)
listen.backlog = 511

If your php-fpm processes are typically full and active, you can instead trigger monitoring or automation based on the number of connections backlogged.  For example you may want to build additional capacity when there is more than 50 connections backlogged.

Conclusion

The php-fpm status page can be used to monitor your application performance and find issues before they happen.  It can also be tied into automation to automatically scale up or down your application tier depending on performance.  Should you enable the status page however, please ensure the page is locked down to trusted IP addresses or password protected, as it does expose information about your application that can be used to construct malicious requests.

I may circle back to this article and add more monitoring/automation examples, but for now I hope this information has helped you.  Please reach out with any comments or questions.