Size: 4749
Comment:
|
← Revision 24 as of 2023-05-25 17:00:50 ⇥
Size: 4297
Comment:
|
Deletions are marked like this. | Additions are marked like this. |
Line 1: | Line 1: |
## page was renamed from PHPFPMSetup | |
Line 4: | Line 3: |
'''PHP-FPM''' is a PHP implementation of the FastCGI, an enhancement of the earlier Common Gateway Interface (CGI). It works especially well with [[NGINX/FastCGIConfiguration|NGINX]]. | The PHP '''FastCGI Process Manager''' ('''PHP-FPM''') is an implementation of the [[Protocols/CGI|FastCGI]] specification. |
Line 14: | Line 13: |
To install PHP-FPM on a system, use your local package to manager to grab all of the following: `php`, `php-fpm`, `fcgiwrap`, and `nginx`. | `php-fpm(8)` naturally depends on `php(1)`. See [[PHP#Installation|here]] for help with installation, and [[PHP/Configuration|here]] for help with configuration. |
Line 16: | Line 15: |
Often `apache2-utils` (a.k.a. `apache-tools`, `httpd-utils`, etc... consult your package manager!) is also necessary, for creating `.htpasswd` files. | Most [[Linux]] and [[BSD]] distributions will offer a `php-fpm` package. |
Line 18: | Line 17: |
Upstream manages a Docker file with frequent security patching, as `bitnami/php-fpm:latest`. This will expose PHP-FPM on port 9000 and generally work out of the box. | Official container images are available from the upstream development team. They are tagged like `php:<version>-fpm` ---- |
Line 22: | Line 23: |
=== PHP === | == Configuration == |
Line 24: | Line 25: |
PHP-FPM unsurprisingly runs in '''PHP''' and will require a working installation. The primary configuration for PHP is found at `/etc/php/php.ini`. Some distributions provide two versions: a hardened `php.ini-production` and a verbose `php.ini-development`. | `php-fpm(8)` listens on one or more ports or sockets. Each point of contact is a '''pool'''. |
Line 26: | Line 27: |
See [[PHPConfiguration|here]] for help in configuring PHP. | 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. |
Line 28: | Line 29: |
The upstream Docker image bundles PHP internally, but it is ''possible'' to un-bundle it and force the use of an existing installation. === PHP-FPM === For the most part, distributed configuration for PHP-FPM work out of the box. |
As an example: |
Line 37: | Line 32: |
; Pid file pid = /run/php-fpm/php-fpm.pid |
[www] user = www-data group = www-data |
Line 40: | Line 36: |
; Error log error_log = /var/log/php-fpm.log |
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 |
Line 46: | Line 48: |
=== TCP Sockets === | |
Line 47: | Line 50: |
=== FCGIWrap === | The above example uses a shorthand: `listen = 9000`. This causes `php-fpm(8)` to listen on all addresses at port 9000. |
Line 49: | Line 52: |
'''FCGIWrap''' is, as the name implies, a wrapper script. It manages the configuration of FastCGI through PHP-FPM so that all you need to do is point NGINX at `/run/fcgiwrap.sock`. | However, `listen.allowed_clients` overrides this with a client whitelist. This should be comma-separated. |
Line 51: | Line 54: |
At the same time, it is ''entirely'' optional. The upstream Docker image does not include it. Not using '''FCGIWrap''' will require more attention on the [[PHPFPMConfiguration|configuration of PHP-FPM]], however. | An IPv4 address is specified like `127.0.0.1:9000`; an IPv6 address is specified like `[::1]:9000`. |
Line 55: | Line 58: |
=== NGINX === | === Unix Sockets === |
Line 57: | Line 60: |
'''NGINX''' is a modern and lightweight web server, which works well with PHP-FPM. For more details on NGINX configuration, see [[NGINXSetup|here]]. A basic site configuration for PHP-FPM would be: |
To use a [[Linux/Networking#Unix_Sockets|Unix socket]] to pass requests, try: |
Line 62: | Line 63: |
user www-data www-data; http { server { listen 80; server_name example.com; root /var/www; location ~ \.php(/|$) { fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param PATH_INFO $fastcgi_path_info; try_files $uri =404; fastcgi_pass unix:/run/fcgiwrap.sock; include fastcgi_params; } } } }}} As stated above, `/run/fcgiwrap.sock` can be used through FCGIWrap. If you are not using that package, or if you are using the upstream Docker image, you will need to set this differently. In particular, if you are redirecting to a PHP environment on another server, you will need to set this to an address and port. {{{ fastcgi_pass 127.0.0.1:9000 |
listen = /run/php-fpm/php-fpm.sock listen.owner = www-data listen.group = www-data listen.mode = 0660 |
Line 92: | Line 71: |
=== Test Script === | === Static Process Management === |
Line 94: | Line 73: |
A minimal test script to validate the PHP installation. | A pool spawns and maintains `pm.max_children` number of processes. |
Line 97: | Line 76: |
<?php phpinfo(); ?> | 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 |
Line 104: | Line 149: |
== Remote Files, chroots, and Work Directories == | == Usage == |
Line 106: | Line 151: |
PHP applications can be placed anywhere on the web root and they will work as expected. This is because PHP-FPM defaults to working in the current work directory. | |
Line 108: | Line 152: |
However, it is ''recommended'' to isolate PHP-FPM by running it in a different work directory. This is accomplished by configuring PHP-FPM on a pool level, which you can read more about [[PHPFPMConfiguration#PoolConfiguration|here]]. What needs to be addressed up-front is how a web server will interact with an isolated FastCGI environment. | |
Line 110: | Line 153: |
The NGINX `try_files` command, as shown below, checks for existence of files. This will cause issues if PHP applications are actually living in a different directory (or a different server). However, without checking for the existence of an executable, you can run into difficult-to-debug errors and security issues regarding embedded PHP in ordinary files. | === Nginx === |
Line 112: | Line 155: |
The workaround is to set the key FastCGI parameters for the target server and check the URI against local null files. Furthermore, note the specific ordering in this configuration. | See [[Nginx/FastCGI#PHP-FPM|here]] for details. === Apache === See [[Apache/FastCGI#PHP-FPM|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: |
Line 115: | Line 172: |
location ~ \.php(/|$) { fastcgi_split_path_info ^(.+?\.php)(/.*)$; fastcgi_param SCRIPT_FILENAME /remote/path/to/work/directory/$fastcgi_script_name; fastcgi_param PATH_INFO $fastcgi_path_info; try_files $uri =404; fastcgi_pass 127.0.0.1:9000; include fastcgi_params; } |
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 |
Line 127: | Line 181: |
Note that `try_files` is called ''strictly after'' path info has been pulled out. Try files will, on success, overwrite `$uri` with the matched local URI. To avoid this, set the value of parameters before validating file existence. | By default, `php-fpm(8)` will not run as root. The `--allow-to-run-as-root` flag may need to be added. ---- == See also == [[https://man.archlinux.org/man/php-fpm.8|php-fpm(8)]] |
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.