PHP-FPM pools give each site its own user and limits

A server hosting six sites through one PHP-FPM pool runs all six as the same user, so a compromised plugin on the least important one can read the database credentials of the other five. The second problem is quieter: they also share one process pool, so a single site with a slow external API can occupy every child and take the rest down with it.

; /etc/php5/fpm/pool.d/catalogue.conf
[catalogue]
user  = catalogue
group = catalogue

listen       = /var/run/php5-fpm-catalogue.sock
listen.owner = www-data
listen.group = www-data
listen.mode  = 0660

pm                   = dynamic
pm.max_children      = 8
pm.start_servers     = 2
pm.min_spare_servers = 2
pm.max_spare_servers = 4
pm.max_requests      = 500

php_admin_value[open_basedir] = /var/www/catalogue:/tmp
php_admin_value[error_log]    = /var/log/php5-fpm/catalogue.log
php_admin_flag[display_errors] = off

pm.max_children is the number to compute rather than copy from a blog post, because it is the hard ceiling on concurrent PHP requests for that site: divide the memory you are prepared to give the site by the average resident size of a child, which ps will tell you and which is usually 30–50 MB on a framework application. Adding a pool does not add memory, so the sum across every pool has to fit with room left for MySQL — this is where a box that was fine with one site starts swapping with four. Two details earn their place. php_admin_value cannot be overridden by ini_set() at runtime while plain php_value can, which is the entire difference for a setting like open_basedir. And a Unix socket per pool beats a shared TCP port because file permissions become the access control: nginx pointed at the wrong socket fails outright instead of silently executing under another site’s user.