Differences between revisions 7 and 23 (spanning 16 versions)
Revision 7 as of 2020-07-11 18:27:11
Size: 4767
Comment:
Revision 23 as of 2023-04-22 20:28:53
Size: 4296
Comment:
Deletions are marked like this. Additions are marked like this.
Line 1: Line 1:
= PHP-FPM Setup = = PHP-FPM =
Line 3: 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 [[NGINXSetup|NGINX]].

See [[PHPFPMConfiguration|here]] for configuration of PHP-FPM.
The PHP '''FastCGI Process Manager''' ('''PHP-FPM''') is an implementation of the [[Protocols/CGI|FastCGI]] specification.
Line 15: 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 17: 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 19: 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 23: Line 23:
=== PHP === == Configuration ==
Line 25: 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 27: 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 29: 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 38: Line 32:
; Pid file
pid = /run/php-fpm/php-fpm.pid
[www]
user = www-data
group = www-data
Line 41: 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 47: Line 48:
=== TCP Sockets ===
Line 48: 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 50: 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 52: 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 56: Line 58:
=== NGINX === === Unix Sockets ===
Line 58: 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 63: 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 93: Line 71:
=== Test Script === === Static Process Management ===
Line 95: Line 73:
A minimal test script to validate the PHP installation. A pool spawns and maintains `pm.max_children` number of processes.
Line 98: 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 = static
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 105: Line 149:
== Remote Files, chroots, and Work Directories == == Usage ==
Line 107: 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 109: 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 111: 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 113: 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 116: 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 128: 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.


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 = static
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.


See also

php-fpm(8)


CategoryRicottone

PHP/FPM (last edited 2023-05-25 17:00:50 by DominicRicottone)