A better way to run PHP-FPM

Mattias Geniar, Wednesday, April 9, 2014 - last modified: Monday, September 21, 2015

If you search the web for PHP-FPM configurations, you'll find many of the same configurations popping up. They nearly all use the 'dynamic' process manager and all assume you will have one master process for running PHP-FPM configurations. While there's nothing technically wrong with that, there is a better way to run PHP-FPM.

In this blogpost I'll detail;

  1. Why 'dynamic' should not be your default process manager
  2. Why it's better to have multiple PHP-FPM masters

Why you should prefer the 'ondemand' Process Manager instead of 'dynamic'

Most of the copy/paste work on the internet for PHP-FPM have configurations such as the one below.

pm = dynamic
pm.max_children = 5
pm.start_servers = 3
pm.min_spare_servers = 2
pm.max_spare_servers = 4
pm.max_requests = 200

Most "guides" advocate the use of the 'dynamic' Process Manager ('pm' option in the config), which allows you to choose how many minimum and maximum (spare) processes you have per pool. Many guides however make blind assumptions on what your server specs are and will cause, like in the example above, a minimum of 3 PHP processes running per pool (pm.start_servers = 3). If you're on a low-traffic site, that could very well be overkill. For your server, it looks like this in your processlist.

root   3986  4704 ?   Ss   19:04   php-fpm: master process (/etc/php-fpm.conf)
user   3987  4504 ?   S    19:04   \_ php-fpm: pool pool_name
user   3987  4504 ?   S    19:04   \_ php-fpm: pool pool_name
user   3987  4504 ?        19:04   \_ php-fpm: pool pool_name

Those 3 processes will always be running, whether they're needed or not.

Ondemand Process Manager

A far better way to run PHP-FPM pools, but badly documented, would be the 'ondemand' Process Manager. As the name suggests, it does not leave processes lingering, but spawns them as they are needed. The configuration is similar to the above, but simplified.

pm = ondemand
pm.max_children = 5
pm.process_idle_timeout = 10s
pm.max_requests = 200

The 'ondemand' process manager was added since PHP 5.3.8. The config above causes your default processlist to look like this.

root   3986  4704 ?   Ss   19:04  php-fpm: master process (/etc/php-fpm.conf)

Only the master process is spawned, there are no pre-forked PHP-FPM processes. Only when processes are needed will they be started, to a maximum of 5 (with the config above, which is defined by pm.max_children). So if there are 2 simultaneous PHP requests going on, the processlist would be:

root   3986  4704 ?   Ss   19:04   php-fpm: master process (/etc/php-fpm.conf)
user   3987  4504 ?   S    19:04   \_ php-fpm: pool pool_name
user   3987  4504 ?   S    19:04   \_ php-fpm: pool pool_name

After the configured timeout in "pm.process_idle_timeout", the process will be stopped again. This does not impact PHP's max_execution_time, because the process manager only considers a process "idle" when it's not serving any request.

If you're working on a high performance PHP setup, the 'ondemand' PM may not be for you. In that case, it's wise to pre-fork your PHP-FPM processes up to the maximum your server can handle. That way, all your processes are ready to serve your requests without needing to be spawned first. However, for 90% of the sites out there, the ondemand PHP-FPM configuration is better than either static or dynamic.

A shared APC or OPcache: why multiple PHP-FPM masters are better

You may not be aware that the APC or OPcache is actually held by the master process in PHP. Any configuration for APC needs to come from the .INI configurations and cannot be overwritten later on via ini_set() or php_admin_value. That's because the spawned PHP-FPM processes have no influence on the size or config of the APC cache, as it is initiated and managed by the master process.

That inherently means that the APC/OPcache cache is shared between all PHP-FPM pools. If you only have a single site to serve, that's no issue. If you have a few dozen sites on the same server via PHP-FPM, you should be aware that they all share the same APC/OPcache cache. The APC or OPcache size should then be big enough to hold the opcode cache of all your sites combined.

To avoid this, each PHP-FPM pool can also be started separately and have it's own master process. That means each site can have its own APC or OPcache and can be started/stopped independently from all the other PHP-FPM pools. A change in one pool's config does not cause all the other FPM pools to be reloaded when the new config needs to be activated, which is the default behaviour of "/etc/init.d/php-fpm reload" (it would reload all pools).

What's needed to achieve this then;

  1. Each PHP-FPM pool needs its own init.d start/stop script
  2. Each PHP-FPM pool needs its own php-fpm.conf file to have a unique PID

If you manage your environment via a CMS such as Puppet/Chef/Salt/Ansible, the above is not difficult to set up. If you do things manually, it can become a burden and difficult to manage.

Looking at the PHP-FPM configuration

Here's what an abbreviated configuration can look like. You would now have a single .conf file that contains the configuration of your master process (PID etc.) as well as the definition of 1 PHP-FPM pool.

$ cat /etc/php-fpm.d/pool1.conf
pid = /var/run/php-fpm/pool1.pid
log_level = notice
emergency_restart_threshold = 0
emergency_restart_interval = 0
process_control_timeout = 0
daemonize = yes

listen = /var/run/php-fpm/pool1.sock
listen.owner = pool1
listen.group = pool1
listen.mode = 0666

user = pool1
group = pool1

pm = ondemand
pm.max_children = 5
pm.process_idle_timeout = 10s
pm.max_requests = 500

The above contains the most important bits; the main config determines that it can be daemonized and where the PID-file should be located. The Pool-configuration has the basic information of where to listen to and the type of Process Manager.

The init.d file is a simple copy/paste from the default /etc/init.d/php-fpm with a few modifications.

$ cat /etc/init.d/php-fpm-pool1
#! /bin/sh
# chkconfig: - 84 16
# description:  PHP FastCGI Process Manager for pool 'pool1'
# processname: php-fpm-pool1
# config: /etc/php-fpm.d/pool1.conf
# pidfile: /var/run/php-fpm/pool1.pid

# Standard LSB functions
#. /lib/lsb/init-functions

# Source function library.
. /etc/init.d/functions

# Check that networking is up.
. /etc/sysconfig/network

if [ "$NETWORKING" = "no" ]
    exit 0


start () {
    echo -n $"Starting $prog: "
    daemon --pidfile ${pidfile} php-fpm --fpm-config=${fpmconfig} --daemonize
    [ $RETVAL -eq 0 ] && touch ${lockfile}
stop () {
    echo -n $"Stopping $prog: "
    killproc -p ${pidfile} php-fpm
    if [ $RETVAL -eq 0 ] ; then
        rm -f ${lockfile} ${pidfile}

restart () {

reload () {
    echo -n $"Reloading $prog: "
    killproc -p ${pidfile} php-fpm -USR2

# See how we were called.
case "$1" in
    status -p ${pidfile} php-fpm
    [ -f ${lockfile} ] && restart || :
    echo $"Usage: $0 {start|stop|status|restart|reload|force-reload|condrestart|try-restart}"

exit $RETVAL

The only pieces we changed to that init.d script are at the top; a new process name has been defined (this needs to be unique) and the PID-file has been changed to point to our custom PID-file for this pool, as it's defined in the pool1.conf file above.

You can now start/stop this pool separately from all the others. It's configuration can be changed without impacting others. If you have multiple pools configured, your process list would look like this.

root      5963  4704 ?        Ss   19:23   0:00 php-fpm: master process (/etc/php-fpm.d/pool1.conf)
root      6036  4744 ?        Ss   19:23   0:00 php-fpm: master process (/etc/php-fpm.d/pool2.conf)

Multiple master processes are running as root and are listening to a socket defined in the pool configuration. As soon as PHP requests are made, they spawn children to handle them and stop them again after 10s of idling. The master process also shows which configuration file it loaded, making it easy to pinpoint the configuration of that particular pool.

Better separation

As soon as PHP requests are made, the processlist looks like this.

root  5963  4704  Ss  19:23  php-fpm: master process (/etc/php-fpm.d/p1.conf)
user  3987  4504  S   19:23   \_ php-fpm: pool pool1
user  3987  4504  S   19:23   \_ php-fpm: pool pool1
root  6036  4744  Ss  19:23  php-fpm: master process (/etc/php-fpm.d/p2.conf)
user  3987  4504  S   19:23   \_ php-fpm: pool pool2

To summarise, the above has 2 main advantages:

  1. a separate APC/OPcache bytecode cache per PHP-FPM pool
  2. the ability to start/stop/reconfigure PHP-FPM pools without impacting the other defined pools

For anyone struggling with APC/OPcache/realpath/stat cache issues on PHP deploys, this configuration could be a solution by allowing (sudo) access to restart or reload the master PHP-FPM process of your particular pool in order to clear all caches.

Things to keep in mind when doing this:

  • Logrotate should be modified if you use error logging in each FPM pool, to use the correct pool's PID to reload the master process;
  • Make sure all your FPM pools start on system boot, as they each have a new init.d script (check via chkconfig --list);

Feedback appreciated!

Hi! My name is Mattias Geniar. I'm a Support Manager at Nucleus Hosting in Belgium, a general web geek & public speaker. Currently working on DNS Spy & Oh Dear!. Follow me on Twitter as @mattiasgeniar.

Share this post

Did you like this post? Will you help me share it on social media? Thanks!


Stéphan Wednesday, April 9, 2014 at 21:40 - Reply

Too bad there isn’t a master to spin up pools (aka other masters) on demand.

This might be a nice thing for shared hosting.
And would finally put my FPM scripts to good use.

Chris Thursday, April 10, 2014 at 11:44 - Reply

Would be nice if the master process doesn’t run as root, but as the pool user. Not sure if that’s possible.

    Mattias Geniar Thursday, April 10, 2014 at 12:07 - Reply

    I’m not sure actually. In “normal” PHP-FPM, it would have to be root to suexec the different pools to their user and manage the socket files. If one master will only serve one pool, technically it should be possible to have that master also run as a non-root user.

    From a security POV an improvement, but I would guess a very small improvement as the master itself only spawns children and never actually handles any PHP requests directly.

Bart Friday, April 11, 2014 at 05:27 - Reply

We run masters in ‘chroot userspec’. Works perfectly. IMHO a lot safer than trusting PHP (Php heartbleed anyone?) as root in /. At least I sleep better :).

jerometang Thursday, May 15, 2014 at 08:16 - Reply

Hi Mattias,

is it common usage for php in enterprise to run multiple apps on one host? with multiple fpm master to manage them.

    Mattias Geniar Thursday, May 15, 2014 at 09:17 - Reply


    For larger sites, only one website would be running on one host and this problem indeed does not occur. However, there are _many_ more small sites than there are big sites, and for them this kind of setup would be an improvement for them.

Jesin A Tuesday, November 18, 2014 at 14:56 - Reply

Hi Mattias,

After PHP bug #67060 was fixed how do we configure per-user pools when listen.mode defaults to 0660?

Do we place the user “pool1” in the www-data group?

usermod -G pool1 www-data

    Mattias Geniar Tuesday, November 18, 2014 at 15:04 - Reply

    You chown the socket to whichever user is running the Apache service. If your Apache service is running as the “httpd” user, chown the socket as httpd.

      Jesin A Tuesday, November 18, 2014 at 15:24 - Reply

      Wow thanks for the quick reply!

      But when php-fpm is restarted the socket owner is set to pool1 again. So do you suggest something like this:

      listen.owner = httpd
      listen.group = httpd

      user = pool1
      group = pool1

      Or this:

      usermod -aG pool1 httpd

      Mattias Geniar Tuesday, November 18, 2014 at 15:25 - Reply

      Correct, either you set the session owner/group in the PHP-FPM pool config, or you change the socket mode back to 0666 (which is unsecure), to allow everyone read/write access to the socket.

      Jesin A Tuesday, November 18, 2014 at 15:44 - Reply

      What is your opinion on adding the web server user to the “pool1” group?

      usermod -aG pool1 www-data

      The ISPConfig control sets listen.owner/group and user/group to the same username and does this:

      # id www-data

      uid=33(www-data) gid=33(www-data) groups=33(www-data),1003(ispapps),1004(ispconfig),1005(client0)

      Mattias Geniar Tuesday, November 18, 2014 at 17:52 - Reply

      The result will be the same, although that would still allow _any_ website to connect to _any_ PHP-FPM pool socket. So you’re bypassing the security of the bugfix altogether, you might as well go back to the default mode of 0666.

      If you want a secure PHP-FPM config, each PHP-FPM pool needs to have a config that only allows the website user to connect to it, and not every user.

      Jesin A Tuesday, November 18, 2014 at 20:52 - Reply

      Oops I said the opposite of what I meant. We are NOT adding the user (pool1) to the webserver group (www-data) we’re doing the opposite here – adding www-data to the secondary group pool1 or pool2.

      So the webserver user has access to all the sockets but the pool users cannot access each other’s socket. Am I correct here?

      Also why did this article and other articles on PHP-FPM on the web modify listen.user/listen.group options when it is enough to change just user/group even before this bug was identified?

John Friday, January 2, 2015 at 03:49 - Reply

Thanks for the ‘ondemand’ thing! I was looking for a way to create a new php-fpm pool in a new user and group when I ran into your post. I’ve got a fairly quiet server, so ondemand frees up a bunch of mostly unused resources.

Your guide was also helpful in pointing me in the right direction to set up an isolated pool.

Juraj Simon Tuesday, January 13, 2015 at 12:26 - Reply

Can you estimate CPU/memory overheat for many (hundreds or thousands) pools when every pool must also have its master process?
One one server I have cca 1000 pools and one master process…
Now this master process now use about ~170MB ram and every child process about ~12MB ram… Did you tested it on something with that many pools?

    Mattias Geniar Tuesday, January 13, 2015 at 18:28 - Reply

    Hi Juraj,

    I don’t have any testing I’m afraid, this would have to be done by yourself. It’ll also depend on the PHP version, the amount of requests your applications are getting, which extensions are loaded, …

    For 1.000+ pools I probably wouldn’t look into this. Unless you can go with a systemd approach with socket activation, then the master processes are only started when they’re needed and they don’t consume memory if no requests are coming in.

Christian Sunday, February 22, 2015 at 20:59 - Reply

the one thing I am still a little confused on is when creating all of these other master processes how are you coorelating them to other “sites” or when a certain site is loaded these just start per that site. I am not seeing the connection here… Therefore if I have 25 sites should I do this 25 x’s. etc.

Andrew Cotton Monday, April 13, 2015 at 19:12 - Reply


Wow, thanks a lot! I’m actually new to nginx+php-fpm and this has pointed me in the direction I believe we want to go. We have been sharing a pair of application servers for 5 applications developed in house. We have grown and have divided labors a bit and have been using fabric (python library) to aid in updates. There is still some evolution necessary here, but is fine for now.

The great part about this is that I have moved our company towards nginx+php-fpm and believe that, if configured as you suggest, we will be able to have multiple developers updating code in production and restarting master php-fpm processes for a specific pool without affecting the other applications. Basically, an update to app1 will not necessitate an apache restart which used to temporarily cause a glitch in service (even though I used graceful; I never understood why graceful was so clumsy–perhaps it was the opcache being refreshed took a moment?).

Thanks for sharing this and helping us improve our process and gain more control over our deployments and improve our availability.

simon Thursday, June 11, 2015 at 02:18 - Reply

Thanks Matthias

The solution you proposed, make my web-site start back to work.
I have a little server on debian with apache 2.4.10 + php5.6

I don’t know why but now with the new apache only one pid shared give me the mess between my pools, and only one web-site was succeded to run each time.

Now i have different pid for different pool (like you proposed) and my php-fpm rocks at last!

And the on demand seems works good.

thks you

Nota :
I have still one problem i cannot access to my root website
i can access to http://www.skiscool.com or fr.skiscool.com but not for http://skiscool.com

And i cannot resolve that i’ve got a 404 not found each time.. i think my host file is good and my bind as well ..
i put in ServerName skiscool.com , and ServerAlias http://www.skiscool.com

but apache still dont want to run the skiscool.com..

Olaf Lederer Friday, July 24, 2015 at 11:54 - Reply

Hoi Mattias,

thanks for this great article about how to use on-demand process manager! I have a question about the number of child processes. Based on a low traffic website how many processes should I use for a “normal” WordPress website? Any experience?

Mattias Geniar Friday, July 24, 2015 at 12:05 - Reply

Hi Olaf,

For small sites, my rule of thumb of 3-5 PHP processes. It usually only needs one, but there are some plugins that fire of several AJAX calls at once, and they all require PHP processing.

So to have a bit of “concurrency” in the amount of simultaneous requests, I usually keep it at 3-5 processes.

Richard Wednesday, August 12, 2015 at 00:51 - Reply

Thanks for the article.
I’ve chopped mysql and php5.6 fpm files on my digitalocean/serverpilot server and ram usage shrinked a lot.
The site doesn’t get many visitors, but it was eating a lot of ram on zero load.
It still needs a lot of configuration, I fear that a 64 OS wasn’t a good choice a 1GB server.

Nik Saturday, December 26, 2015 at 01:27 - Reply

hi :)

i am testing your tips with apache2.4, PHP 5.6.16-2 , Zend Engine v2.6.0, Zend OPcache v7.0.6-dev, php5-fpm. I am trying to figure out how can i define different opcache configs following your instructions. It is fine until the definitions of different master processes, one for every pool. The opcache is indeed separated and not shared any more.

but is there a way to config different cache sizes for example? The php.ini file is still one

thanks :)

Gabriel Tuesday, January 5, 2016 at 22:12 - Reply

In a dedicated server I suggest to cofigure PHP-FPM with STATIC process manager: just start all possible child processes allowed by the available memory.

    Jack Saturday, June 2, 2018 at 15:07 - Reply

    Please don’t do this. Your energy consumption will increase, and your hardware will be being stressed 24/7, 365. Sure fire way to burn out components and max out your electricity bill. The planet will thank you too!

David Levy Sunday, May 22, 2016 at 00:16 - Reply

I followed your tutorial and after making all the changes, /etc/init.d/php5-fpm-example will not start or report status or anything of that nature, what am I missing?? or how do I troubleshoot this without new logs being created as it’s supposed to?

Adam Monday, August 1, 2016 at 00:18 - Reply

Small correction (update?) regarding: “You may not be aware that the APC or OPcache is actually held by the master process in PHP. Any configuration for APC needs to come from the .INI configurations and cannot be overwritten later on via ini_set() or php_admin_value.”

No idea about APC, but in OPcache you can set almost everything via php_admin_value in the FPM pool config, and some options can be set also via ini_set() — see http://php.net/manual/en/opcache.configuration.php

bdraco Wednesday, September 14, 2016 at 08:02 - Reply

Ondemand is not recommended until this bug is fixed.


olivedev Friday, November 25, 2016 at 15:53 - Reply

In dedicated server, if you are using a platform like this one (https://www.cloudways.com/en/php-cloud-hosting.php ), you won’t have to manually install and configure PHP-FPM. This is because these packages are already installed and optimized. What would you recommend?

cherri3a Sunday, February 12, 2017 at 20:01 - Reply

first thank’s but wheni change all configuration like this the server can’t save lot of users in the same time

Cloudnine Wednesday, March 22, 2017 at 09:53 - Reply

Do you see the blacklist option in Opcache as a way to ensure that certain sites on the server don’t get to share the cache? Many of my clients use the same server for their dev and production sites to keep the costs down and I use the blacklist option to make sure that the cache is used only for the production site.

jeff caar Saturday, April 1, 2017 at 12:33 - Reply

Hey Mattias :)

“If you’re working on a high performance PHP setup, the ‘ondemand’ PM may not be for you.”

Considering the typical WordPress site, let’s say running a LEMP stack on a dedicated VPS server (not sharing the server with any other sites), would “ondemand” still make sense, or “dynamic”, assuming that the site is getting at least several hundred or few thousand page loads or visitors each day? Thanks!

    Per Monday, August 7, 2017 at 11:05 - Reply

    Yes, I manage quite a few websites both Drubal and WP, most of them get’s about 400K to 800K visitors per day and on demand is definitely still worth running.

      Mattias Geniar Monday, August 7, 2017 at 16:59 - Reply

      Keep in mind though that with that many visitors, it’ll behave more like “dynamic” than “ondemand”. Ondemand implies that, with 0 visitors, the child processes get killed and it won’t consume any memory. With sufficient visitors, you’ll always have _some_ child processes active.

      The bottleneck with ondemand is the potentially slow startup of child processes on the first hit, so as long as you keep that process/cache “hot”, there is hardly any difference between dynamic vs. ondemand.

Stéphan Tuesday, April 3, 2018 at 14:17 - Reply

Did some tests:

pm = dynamic
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 3
pm.max_spare_servers = 20

I fail to see the use of pm.min_spare_servers:
When the pool starts, it is at 5 children ( pm.start_servers ).
If I leave the pool like this and never hit nginx with any requests, the pool just stays at 5 children and doesn’t fall back to pm.min_spare_servers
When I hit nginx with ab, the pool jumps up to 50 children and once ab stops sending load the pool goes down to 20 children ( pm.max_spare_servers ).
When I leave this pool over night, the pool is still at 20 children ( pm.max_spare_servers ) the next day. (nginx is listening on localhost only).

I can’t seem to get the pool to go back down to 3 childs ( pm.min_spare_servers ).
When I reload ( not restart ) the FPM pool, I count:

# pgrep -f 'php-fpm: pool www' | wc -l

This is pm.start_servers

Also looking at the C-code of FPM: https://github.com/php/php-src/blob/master/sapi/fpm/fpm/fpm_process_ctl.c
I really can’t see anything pointing to any use of pm.min_spare_servers

Very strange!

Anyone who knows FPM better than me that could shed some light on this?

Tested on CentOS 7.4 with php(BASE), php-fpm(BASE) and nginx(EPEL) packages.

    Jack Saturday, June 2, 2018 at 15:14 - Reply

    Sounds like your processes are getting hung up. Probably some PHP is getting stuck, and I’m assuming it doesn’t have a timeout set.

    Try adding:

    pm.max_requests = 100

    And see if that makes a difference. After a process handles 100 requests, it will terminate the process and spawn a new one. This is good practice as if anything clogs up the process, it can be cleared out before permanently tying up the resources (as it appears yours is doing).

ip finder Sunday, March 10, 2019 at 04:09 - Reply

can’t save lot of users in the same time
Very strange!

but thank any way
PHP-FPM Tip 2. – PHP-FPM Global Configuration Tweaks
Set up emergency_restart_threshold, emergency_restart_interval and process_control_timeout. Default values for these options are totally off, but I think it’s better use these options example like following:

emergency_restart_threshold 10
emergency_restart_interval 1m
process_control_timeout 10s

Luke Monday, June 17, 2019 at 13:06 - Reply

Hi Mattias,
Would it be possible to provide an updated version of the tutorial above but related to php7.0-fpm since certain structure is not the same as in the one install on Ubuntu 18?

Thanks in advance.

Leave a Reply

Your email address will not be published. Required fields are marked *

Inbound links