PHP-FPM
The PHP FastCGI Process Manager (PHP-FPM) is an implementation of the FastCGI specification.
Contents
Installation
php-fpm(8) naturally depends on php(1). See here for help with installation, and here for help with configuration.
Most Linux and BSD distributions will offer a php-fpm package.
Official container images are available from the upstream development team. They are tagged like php:<version>-fpm
Configuration
php-fpm(8) listens on one or more ports or sockets. Each point of contact is a pool.
Each pool should have it's own configuration file under /etc/php/php.fpm.d. A pool's name ($pool) is derived from the section header.
As an example:
[www] user = www-data group = www-data listen = 9000 listen.allowed_clients = 127.0.0.1, 192.168.86.1 pm = dynamic pm.max_children = 5 pm.start_servers = 2 pm.min_spare_servers = 1 pm.max_spare_servers = 3
TCP Sockets
The above example uses a shorthand: listen = 9000. This causes php-fpm(8) to listen on all addresses at port 9000.
However, listen.allowed_clients overrides this with a client whitelist. This should be comma-separated.
An IPv4 address is specified like 127.0.0.1:9000; an IPv6 address is specified like [::1]:9000.
Unix Sockets
To use a Unix socket to pass requests, try:
listen = /run/php-fpm/php-fpm.sock listen.owner = www-data listen.group = www-data listen.mode = 0660
Static Process Management
A pool spawns and maintains pm.max_children number of processes.
pm = static pm.max_children = 5
Dynamic Process Management
A pool spawns pm.start_servers processes. At any given time, some number are 'idle'. The pool tries to keep this number within the range of pm.min_spare_servers to pm.max_spare_servers, never surpassing pm.max_children.
pm = dynamic pm.max_children = 5 pm.start_servers = 2 pm.min_spare_servers = 1 pm.max_spare_servers = 3
On-Demand Process Management
A pool spawns processes on-demand up until pm.max_children. A process is killed after pm.process_idle_timeout has passed.
pm = ondemand pm.max_children = 5 pm.process_idle_timeout = 10s
Process Termination
php(1) scripts are known to leak memory frequently. As such, a regular task is to terminate long-running processes.
A simple solution is to set pm.max_requests to any number other than 0. Processes are killed after handling this number of requests.
Hardening
As a security measure, the allowable script extensions should be set as strictly as possible.
security.limit_extensions = .php .html .htm
A pool can be hardened by isolating it to a distinct working directory, or even a chroot.
chroot = /absolute/path/to/chroot # or chdir = relative/or/absolute/path/to/working/directory
By default, php-fpm(8) clears all environment variables. This can be disabled, but a better strategy is to set specific environment variables.
env[HOSTNAME] = $HOSTNAME env[TMP] = /tmp
The same is true of PHP system variables.
php_admin_value[error_log] = /var/log/php-fpm.log
Usage
Nginx
See here for details.
Apache
See here for details.
Containers
When containerizing a php(1) service, the web server should be kept separate. See other articles for help in configuring the web server containers.
A template container image recipe looks like:
FROM php:fpm-alpine RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" COPY myapp/ /myapp/ COPY confs/ /usr/local/etc/php-fpm.d/ EXPOSE 9000 WORKDIR /myapp CMD php-fpm --nodaemonize
By default, php-fpm(8) will not run as root. The --allow-to-run-as-root flag may need to be added.