Ansible Role linuxfabrik.lfops.php¶
This role installs and configures PHP (and PHP-FPM) on the system, optionally with additional modules.
By default this role does not select a PHP version. It installs the latest version the configured repos offer. On RedHat that is deterministic, because the module stream pins the version at repo level: use linuxfabrik.lfops.repo_remi beforehand to choose it.
Debian has no repo-level equivalent. Its unversioned metapackages (php-cli, php-fpm, php-curl, ...) point at whatever the configured repo declares as its default, which never moves within a Debian release but does move with the sury repo, whenever sury promotes a new PHP version. On a sury host an ordinary apt upgrade therefore migrates PHP to a new major version without anyone deciding to. Set php__version to prevent that.
Consuming roles that inject php__modules__dependent_var on Debian must build the package names from the detected version, for example php{{ __php__installed_version }}-curl, because the unversioned names would reintroduce exactly that drift. See roles/nextcloud/vars/main.yml for the platform-keyed pattern.
This role is compatible with the following PHP versions:
- 7.2
- 7.3
- 7.4
- 8.0
- 8.1
- 8.2
- 8.3
- 8.4
- 8.5
Rules of thumb:
- Specify memory values in MB (M).
memory_limitshould be larger thanpost_max_size.post_max_sizecan stay at16M, even if you haveupload_max_filesize>10000Mfor example.- If disabling
opcache.validate_timestamps,opcache.revalidate_freqis ignored.
This role never exposes to the world that PHP is installed on the server, no matter what.
Available since LFOps 2.0.0.
How the Role Behaves¶
- On RedHat the role ships a small SELinux policy module,
lfops_php_fpm_slowlog, and hands it to theselinuxrole throughphp__selinux__modules__dependent_var. It grants thehttpd_tdomain thesys_ptracecapability andptraceon itself, which the PHP-FPM master needs to read the backtrace out of a worker that exceededphp__fpm_pool_conf_request_slowlog_timeout__*_var. Without it, PHP-FPM logsfailed to ptrace(ATTACH) child <pid>: Operation not permitted (1)and leaves the slowlog empty. The targeted policy grants neither permission and offers no boolean for it, so the rules have to come from a module. - The module is installed regardless of the configured
request_slowlog_timeout, so that turning the slowlog on later is a pure configuration change. The permissions it grants apply to the wholehttpd_tdomain, Apache httpd included. Its rules are unconditional and therefore not subject to thedeny_ptraceboolean: on a host hardened withsetsebool -P deny_ptrace on,httpd_tcan still ptrace itself. Setphp__skip_selinux: truein the playbook to leave the host's policy untouched. - Every pool using the default
filessession handler gets a dedicated session directory below the distribution's session base (/var/lib/php/sessionon RedHat,/var/lib/php/sessionson Debian), owned by the pool'suserandgroupwith mode0700, so pools cannot read each other's sessions. On RedHat the/var/lib/php/session(/.*)?file context gives it thehttpd_var_run_ttype php-fpm needs. On Debian the packagedsessioncleantimer recurses the session base using the globalsession.gc_maxlifetime, so a per-poolsession.gc_maxlifetimeis not honored by the cleanup there, and a session that stays open but idle longer than the lifetime may be removed. - Each pool writes its
error_logandslowloginto a per-service log directory (/var/log/php-fpmon RedHat,/var/log/<service>on Debian, e.g./var/log/php8.4-fpm), which the role creates. On RedHat the package's logrotate config already rotates/var/log/php-fpm/*log; on Debian the role ships/etc/logrotate.d/linuxfabrik-php-fpmfor the per-pool logs, since the packaged config only covers the single global log file. - The
[global]section of the PHP-FPM configuration is deployed asz00-linuxfabrik-global.confnext to the pools, becausephp-fpm.confitself belongs to the package. Which side wins depends on where the packagedphp-fpm.confputs itsinclude=line, and the families differ: RedHat reads the pool directory before its own[global], so the package overrides the drop-in, while Debian reads it after and the drop-in overrides the package. The role therefore sets only directives that no packagedphp-fpm.conftouches (log_leveland theemergency_restart_*pair), which take effect on both.error_log,pidanddaemonizeare deliberately left out: they are exactly what the packages set, so setting them here would move them on Debian and silently do nothing on RedHat. - With
pm = dynamicthe master checks once per second whetherpm.min_spare_serversworkers are idle. If not, it forks a batch of workers and doubles the batch size for the next check, up topm.max_spawn_rate(32). From a batch size of 8 on, every one of those checks logsseems busy (you may need to increase pm.start_servers, or pm.min/max_spare_servers)at warning level, and unlike theserver reached pm.max_children settingwarning beside it, this one has no once-only guard, so it repeats every second for as long as the shortfall lasts. The batch size falls back to 1 as soon as the pool haspm.min_spare_serversidle workers again, and also whenpm.max_childrenis reached, so the warning needs four consecutive seconds of shortfall before it appears at all. An ordinary traffic spike therefore writes a block of these warnings while the pool still had capacity to spare:server reached pm.max_children settingis the saturation signal,seems busyonly a hint that the pool could not refill its idle reserve fast enough. Raisingphp__fpm_pool_conf_pm_start_servers__*_varandphp__fpm_pool_conf_pm_min_spare_servers__*_vardoes that, at the cost of that many resident workers per pool; on a host where spikes are normal, pass--ignore='seems busy'to thephp-fpm-logfileMonitoring Plugin instead. - Each pool listens on its own Unix socket below the FPM runtime directory (
/run/php-fpm/<pool>.sockon RedHat,/run/php/<pool>.sockon Debian). The socket belongs torootand carries a POSIX ACL entry for the web server user (listen.acl_users), so a pool running as its own user still hands the web server access without either of them owning the socket. On Debian this deviates from the packaged pool file, which useslisten.owner/listen.groupinstead. On Debian the packaged php-fpm systemd unit additionally maintains a version-agnosticupdate-alternativesalias at/run/php/php-fpm.sockpointing at the socket of the defaultwwwpool. That alias only ever trackswww, so configure the web server with the explicit per-pool socket path rather than the generic one. RedHat ships no such alias. - The role orders Apache after PHP-FPM at boot with the drop-in
/etc/systemd/system/httpd.service.d/z00-php.conf(apache2.service.don Debian). The RHEL package only makes httpd pull in php-fpm and Debian does not even that, so both otherwise start side by side, and the first requests after a reboot fail withAH02454: FCGI: attempt to connect to Unix domain socket ... faileduntil the PHP-FPM socket exists. On a host without Apache the ordering is ignored. It takes effect at the next boot, without a restart. - The pool sets
env[PATH], which upstream leaves unset: withclear_envat its default the worker environment is empty, sogetenv("PATH")returns nothing, which trips applications that shell out and fails Nextcloud's "PHP getenv" setup check. - Every pool gets its own WSDL cache directory below
/var/lib/php/wsdlcache, owned like its session directory. The SOAP extension caches parsed WSDL files there, so aSoapClientdoes not refetch and reparse the service description on every request. PHP's own default is/tmp, which on RedHat means the php-fpm unit'sPrivateTmpnamespace and therefore a cache thrown away on every restart, and on Debian the shared/tmp. Only relevant to applications usingSoapClient: without thesoapextension installed the setting is inert, andini_get('soap.wsdl_cache_dir')returns an empty string. - Every pool answers on the same two FPM-internal endpoints,
/fpm-status(the status page) and/fpm-ping(a liveness check returningpong). Which pool answers is decided by the socket the request arrives on, not by the path, so a second pool is published by giving it its ownLocationpointing at that pool's socket while keeping/fpm-statusas the path sent to FPM. ThelocalhostvHost of the apache_httpd role does this for thewwwpool, which is what thephp-fpm-statusandphp-fpm-pingMonitoring Plugins check by default;php-fpm-statusaccepts--urlseveral times for the additional pools. Both endpoints are reachable for anyone who can reach the pool socket, so take care with a vHost that forwards its whole URI space to a pool, or with a reverse proxy that passes unknown paths through. Setpm_status_pathand / orping_pathto an empty string to turn them off for a pool.
Known Limitations¶
- Setting a pool to
state: 'absent'removes its pool configuration file, but leaves its session directory and itserror_log/slowlogbehind. Remove them by hand once the pool is gone for good.
Dependent Roles¶
Any LFOps playbook that installs this role runs these for you. Optional ones can be disabled via the playbook's skip variables.
- On RedHat, the
lfops_php_fpm_slowlogpolicy module must be compiled and installed (roles: linuxfabrik.lfops.policycoreutils and linuxfabrik.lfops.selinux). - Optional: The EPEL repository, and CRB on Rocky 9 and newer, must be enabled (roles: linuxfabrik.lfops.repo_epel and linuxfabrik.lfops.repo_baseos). Remi's packages link against EPEL content: on RedHat 8 for example
php-opcacheneedslibcapstone, which neither the default repositories nor PowerTools carry. - Optional: Remi's RPM repository (role: linuxfabrik.lfops.repo_remi) provides newer PHP versions.
Tags¶
php
- Installs php, php-fpm and composer.
- Installs and removes the configured PHP modules.
- Deploys the
z00-linuxfabrik.inifor every SAPI. - Deploys and removes the PHP-FPM pools and the
[global]drop-in, together with their session, opcache and log directories. - Deploys the systemd drop-in that orders Apache after PHP-FPM at boot.
- Deploys the logrotate configuration for the per-pool logs (Debian only).
- Manages the state of the php-fpm service.
- Pins the
php,pharandphar.pharalternatives (Debian withphp__versionset only). - Triggers: php-fpm.service restart.
php:alternatives
- Debian with
php__versionset only. Pins thephp,pharandphar.pharalternatives to the declared version, so installing another version does not silently switch the CLI. - Triggers: none.
php:fpm
- Deploys and removes the PHP-FPM pools, and the
[global]drop-in next to them. On Debian these live under the declared version's tree, on RedHat under/etc/php-fpm.d. - Deploys the systemd drop-in that orders Apache after PHP-FPM at boot.
- Creates the shared opcache directory, the php-fpm log directory and one session directory per pool, and relabels them on SELinux hosts.
- Triggers: php-fpm.service restart.
php:ini
- Deploys the
z00-linuxfabrik.ini. RedHat has a single/etc/php.d, Debian one conf.d per SAPI (apache2, cli and fpm) below the declared version's tree. - Deploys the PHP-FPM pools, since they take over several
php__ini_*values, such asmemory_limit. - Triggers: php-fpm.service restart.
php:logrotate
- Debian only. Deploys
/etc/logrotate.d/linuxfabrik-php-fpmfor the per-pool logs. On RedHat the packaged logrotate configuration already covers them. - Triggers: none.
php:modules
- Installs and removes the PHP modules from
php__modules__combined_var. - Triggers: none.
php:state
- Enables or disables the php-fpm service and brings it into the state requested by
php__fpm_service_state. - Triggers: none.
php:update
- Updates the PHP packages, composer and the PHP modules, and reasserts the ini, the pools and their
[global]drop-in, the logrotate configuration, the service state and the alternatives. Do not forget to update the repo beforehand. - On Debian with
php__versionset, this is also how a major version change is carried out: raisephp__version, then run this tag. It installs the declared version, moves the pools, alternatives and the FPM service over to it, and purges the stacks of all other versions. - Triggers: php-fpm.service restart.
Optional Role Variables¶
php__fpm_service_enabled
- Enables or disables the php-fpm service, analogous to
systemctl enable/disable. - Type: Bool.
- Default:
true
php__fpm_service_state
- Changes the state of the php-fpm service, analogous to
systemctl start/stop/restart/reload. - Type: String. One of
reloaded,restarted,started,stopped. - Default:
'started'ifphp__fpm_service_enabledistrue, else'stopped'
php__modules__host_var / php__modules__group_var
- List of dictionaries containing additional PHP modules that should be installed via the standard package manager.
- For the usage in
host_vars/group_vars(can only be used in one group at a time). - Type: List of dictionaries.
- Default:
[] -
Subkeys:
-
name:- Mandatory. Name of the module package.
- Type: String.
-
state:- Optional. State of the module package. Possible options:
absent,present. - Type: String.
- Default:
'present'
- Optional. State of the module package. Possible options:
-
php__version
- Debian only. The PHP version this host runs, for example
'8.4'. Makes the role install the versioned packages (php8.4-cli,php8.4-fpm, ...) instead of the unversioned metapackages, pin thephpalternatives to it, and purge other versions onphp:update. Empty adopts whatever version the configured repos provide, which is correct without the sury repo. Has no effect on RedHat, where the module stream pins the version at repo level. - Type: String.
- Default:
''
Example:
# optional
php__fpm_service_enabled: true
php__fpm_service_state: 'started'
php__fpm_pools__host_var:
- name: 'librenms'
user: 'librenms'
group: 'librenms'
raw: |-
env[PATH] = /usr/local/bin:/usr/bin:/bin
php__modules__host_var:
- name: 'php-mysqlnd'
state: 'present'
php__version: '8.4'
Optional Role Variables - php__ini_* Config Directives¶
Variables for php.ini directives and their default values, defined and supported by this role.
php__ini_date_timezone__group_var / php__ini_date_timezone__host_var
- The default timezone used by all date/time functions. php.net
- Type: String.
- Default:
'Europe/Zurich'
php__ini_default_socket_timeout__group_var / php__ini_default_socket_timeout__host_var
- Default timeout in seconds for socket based streams (e.g. HTTP, FTP). php.net
- Type: Number.
- Default:
10
php__ini_display_errors__group_var / php__ini_display_errors__host_var
- This determines whether errors should be printed to the screen as part of the output or if they should be hidden from the user. This is a feature to support your development and should never be used on production systems (e.g. systems connected to the internet). php.net
- Type: String.
- Default:
'Off'
php__ini_display_startup_errors__group_var / php__ini_display_startup_errors__host_var
- Even when display_errors is on, errors that occur during PHP's startup sequence are not displayed. It's strongly recommended to keep this off. php.net
- Type: String.
- Default:
'Off'
php__ini_error_reporting__group_var / php__ini_error_reporting__host_var
- Set the error reporting level. php.net
- Type: String.
- Default:
'E_ALL & ~E_NOTICE & ~E_DEPRECATED'
php__ini_max_execution_time__group_var / php__ini_max_execution_time__host_var
- This sets the maximum time in seconds a script is allowed to run before it is terminated by the parser. This helps prevent poorly written scripts from tying up the server. The default setting is 30. When running PHP from the command line the default setting is 0. php.net
- The PHP-FPM pools also set it as
php_admin_value[max_execution_time], which takes precedence over thez00-linuxfabrik.ini. Setphp_admin_value_max_execution_timeinphp__fpm_pools__*_varfor a different value per pool. - Type: Number.
- Default:
30
php__ini_max_file_uploads__group_var / php__ini_max_file_uploads__host_var
- The maximum number of files allowed to be uploaded simultaneously. php.net
- Type: Number.
- Default:
50
php__ini_max_input_time__group_var / php__ini_max_input_time__host_var
- This sets the maximum time in seconds a script is allowed to parse input data, like POST and GET. Timing begins at the moment PHP is invoked at the server and ends when execution begins. The default setting is -1, which means that max_execution_time is used instead. Set to 0 to allow unlimited time. php.net
- Type: Number.
- Default:
-1
php__ini_max_input_vars__group_var / php__ini_max_input_vars__host_var
- How many input variables may be accepted (limit is applied to
$_GET,$_POSTand$_COOKIEsuperglobal separately). Use of this directive mitigates the possibility of denial of service attacks which use hash collisions. If there are more input variables than specified by this directive, an E_WARNING is issued, and further input variables are truncated from the request. php.net - The PHP-FPM pools also set it as
php_admin_value[max_input_vars], which takes precedence over thez00-linuxfabrik.ini. Setphp_admin_value_max_input_varsinphp__fpm_pools__*_varfor a different value per pool. - Type: Number.
- Default:
1000
php__ini_memory_limit__group_var / php__ini_memory_limit__host_var
- This sets the maximum amount of memory in bytes that ONE RUNNING SCRIPT is allowed to allocate. This helps prevent poorly written scripts for eating up all available memory on a server. Note that to have no memory limit, set this directive to -1. Again: PHP memory_limit is per-script, just as a highway's speed limit is per-vehicle. php.net
- The PHP-FPM pools also set it as
php_admin_value[memory_limit], which takes precedence over thez00-linuxfabrik.ini. Setphp_admin_value_memory_limitinphp__fpm_pools__*_varfor a different value per pool. - Type: String.
- Default:
'128M'
php__ini_opcache_blacklist_filename__group_var / php__ini_opcache_blacklist_filename__host_var
- A blacklist file is a text file containing the names of files that should not be accelerated, one per line. Wildcards are allowed, and prefixes can also be provided. Lines starting with a semi-colon are ignored as comments. php.net
- Type: String.
- Default:
'/etc/opcache.blacklist'
php__ini_opcache_enable__group_var / php__ini_opcache_enable__host_var
- Enables the opcode cache. When disabled, code is not optimised or cached. php.net
- Type: Number.
- Default:
1
php__ini_opcache_enable_cli__group_var / php__ini_opcache_enable_cli__host_var
- Enables the opcode cache for the CLI version of PHP. php.net
- Type: Number.
- Default:
1
php__ini_opcache_huge_code_pages__group_var / php__ini_opcache_huge_code_pages__host_var
- Enables or disables copying of PHP code (text segment) into HUGE PAGES. This should improve performance, but requires appropriate OS configuration. php.net
- Type: Number.
- Default:
0
php__ini_opcache_interned_strings_buffer__group_var / php__ini_opcache_interned_strings_buffer__host_var
- The amount of memory used to store interned strings, in megabytes. php.net
- Type: Number.
- Default:
12
php__ini_opcache_max_accelerated_files__group_var / php__ini_opcache_max_accelerated_files__host_var
- The maximum number of keys (and therefore scripts) in the OPcache hash table. The actual value used will be the first number in the set of prime numbers { 223, 463, 983, 1979, 3907, 7963, 16229, 32531, 65407, 130987, 262237, 524521, 1048793 } that is greater than or equal to the configured value. php.net
- Type: Number.
- Default:
7963
php__ini_opcache_memory_consumption__group_var / php__ini_opcache_memory_consumption__host_var
- The size of the shared memory storage used by OPcache, in megabytes. The minimum permissible value is "8", which is enforced if a smaller value is set. php.net
- Type: Number.
- Default:
128
php__ini_opcache_revalidate_freq__group_var / php__ini_opcache_revalidate_freq__host_var
- How often to check script timestamps for updates, in seconds. 0 will result in OPcache checking for updates on every request. php.net
- Type: Number.
- Default:
60
php__ini_opcache_save_comments__group_var / php__ini_opcache_save_comments__host_var
- If disabled, all documentation comments will be discarded from the opcode cache to reduce the size of the optimised code. Disabling this configuration directive may break applications and frameworks that rely on comment parsing for annotations. php.net
- Type: Number.
- Default:
1
php__ini_opcache_validate_timestamps__group_var / php__ini_opcache_validate_timestamps__host_var
- If enabled, OPcache will check for updated scripts every opcache.revalidate_freq seconds. When this directive is disabled, you must reset OPcache manually via opcache_reset(), opcache_invalidate() or by restarting the Web server for changes to the filesystem to take effect. php.net
- Type: Number.
- Default:
1
php__ini_post_max_size__group_var / php__ini_post_max_size__host_var
- Sets max size of post data allowed. This setting also affects file upload. To upload large files, this value must be larger than upload_max_filesize. php.net
- The PHP-FPM pools also set it as
php_admin_value[post_max_size], which takes precedence over thez00-linuxfabrik.ini. Setphp_admin_value_post_max_sizeinphp__fpm_pools__*_varfor a different value per pool. - Type: String.
- Default:
'8M'
php__ini_session_cookie_httponly__group_var / php__ini_session_cookie_httponly__host_var
- Marks the session cookie as HttpOnly, so it is not accessible to JavaScript via
document.cookie, mitigating cookie theft via XSS. php.net - The PHP-FPM pools also set it as
php_value[session.cookie_httponly], which takes precedence over thez00-linuxfabrik.ini. Setphp_value_session_cookie_httponlyinphp__fpm_pools__*_varfor a different value per pool. - Type: String.
- Default:
'On'
php__ini_session_cookie_samesite__group_var / php__ini_session_cookie_samesite__host_var
- The
SameSiteattribute of the session cookie, which decides on which cross-site requests the browser sends it. One ofLax,Strict,Noneor an empty string. PHP only validates the value when a script sets it throughini_set(); read from an ini file it is written into theSet-Cookieheader verbatim, so a typo silently ships a nonsense attribute that browsers then ignore. The role'smeta/argument_specs.ymltherefore restricts the variable to the four accepted values and aborts at role entry instead. php.net Laxsends the cookie on same-site requests and on top-level cross-site navigations, but not on cross-site POSTs, iframes or XHR, which is what stops a foreign page from acting under the visitor's session.Strictwithholds it on a top-level navigation as well, so a user following a link from an email lands logged out until the next click.Noneswitches the protection off and only works together withphp__ini_session_cookie_secure__*_var, otherwise browsers drop the cookie entirely. An empty string emits no attribute and leaves the decision to the browser, which differs between Chrome, Firefox and Safari.- Set
None(withSecure) for an application whose identity provider returns through a cross-site POST, as SAML HTTP-POST binding and the OIDCform_postresponse mode do: withLaxthe callback arrives without the session and the login loops. On a host serving more than one application, set it for that pool alone viaphp_value_session_cookie_samesiteinphp__fpm_pools__*_varinstead of host-wide. Applications embedded from another registrable domain need it too. An identity provider or an embedded service under the same registrable domain, for example Collabora atoffice.example.cominside Nextcloud atcloud.example.com, counts as same-site and is unaffected. - Only deployed from PHP 7.3 on, since PHP 7.2 does not know the directive.
- The PHP-FPM pools also set it as
php_value[session.cookie_samesite], which takes precedence over thez00-linuxfabrik.ini. Setphp_value_session_cookie_samesiteinphp__fpm_pools__*_varfor a different value per pool. - Type: String.
- Default:
'Lax' - Deviates from the upstream default, which is an empty string up to PHP 8.5 and therefore emits no attribute at all. PHP itself moves to
Laxin 8.6, so this anticipates the upstream default rather than departing from it.
php__ini_session_cookie_secure__group_var / php__ini_session_cookie_secure__host_var
- Marks the session cookie
Secure, so a browser only ever sends it back over HTTPS. php.net - Set it to
'Off'for a site genuinely served over plain HTTP, which otherwise cannot log anyone in: the browser accepts the cookie and then never returns it. On a host serving both, set it for the affected pool alone viaphp_value_session_cookie_secureinphp__fpm_pools__*_var. - What decides is the scheme the browser uses, not what PHP sees. A site behind a reverse proxy that terminates TLS and forwards plain HTTP is an HTTPS site for this purpose, and is exactly the case where PHP cannot work the flag out for itself.
- Switching an HTTPS host from
'Off'to'On'logs nobody out. PHP only sendsSet-Cookiewhen it creates a session ID, so a running session resumes untouched and its existing cookie keeps its old attributes until the application regenerates the ID, usually at the next login, or the session expires. - The PHP-FPM pools also set it as
php_value[session.cookie_secure], which takes precedence over thez00-linuxfabrik.ini. Setphp_value_session_cookie_secureinphp__fpm_pools__*_varfor a different value per pool. - Type: String.
- Default:
'On' - Deviates from the upstream default
Off: without the flag the session ID travels in cleartext on anyhttp://request to the host, before a redirect to HTTPS can fire, and LFOps deploys these sites behind TLS. PHP sets the flag on its own only when it sees HTTPS itself, which it does not when TLS is terminated in front of it.
php__ini_session_gc_maxlifetime__group_var / php__ini_session_gc_maxlifetime__host_var
- Number of seconds after which session data is treated as garbage and cleaned up by the session garbage collector. php.net
- Type: Number.
- Default:
1440
php__ini_session_sid_length__group_var / php__ini_session_sid_length__host_var
- Length of the session ID string. Only takes effect on PHP versions that still honor the directive; PHP deprecates any value other than the built-in
32. php.net - Type: Number.
- Default:
32
php__ini_session_trans_sid_tags__group_var / php__ini_session_trans_sid_tags__host_var
- HTML tags whose attributes are rewritten to include the session ID when transparent SID support is enabled. php.net
- Type: String.
- Default:
'a=href,area=href,frame=src,input=src,form=fakeentry'
php__ini_smtp__group_var / php__ini_smtp__host_var
- Host used by the
mail()function to send mail (Windows only; ignored on Unix, where thesendmail_pathbinary is used). php.net - Type: String.
- Default:
'localhost'
php__ini_upload_max_filesize__group_var / php__ini_upload_max_filesize__host_var
- The maximum size of an uploaded file. php.net
- The PHP-FPM pools also set it as
php_admin_value[upload_max_filesize], which takes precedence over thez00-linuxfabrik.ini. Setphp_admin_value_upload_max_filesizeinphp__fpm_pools__*_varfor a different value per pool. - Type: String.
- Default:
'2M'
Note that setting php__ini_opcache_huge_code_pages__group_var or php__ini_opcache_huge_code_pages__host_var to 1 might require enabling the SELinux boolean httpd_execmem on RHEL systems.
Example:
# optional
php__ini_date_timezone__host_var: 'Europe/Zurich'
php__ini_default_socket_timeout__host_var: 10
php__ini_display_errors__host_var: 'Off'
php__ini_display_startup_errors__host_var: 'Off'
php__ini_error_reporting__host_var: 'E_ALL & ~E_NOTICE & ~E_DEPRECATED'
php__ini_max_execution_time__host_var: 3600
php__ini_max_file_uploads__host_var: 100
php__ini_max_input_time__host_var: -1
php__ini_max_input_vars__host_var: 1000
php__ini_memory_limit__host_var: '1024M'
php__ini_opcache_blacklist_filename__host_var: '/etc/opcache.blacklist'
php__ini_opcache_enable__host_var: 1
php__ini_opcache_enable_cli__host_var: 1
php__ini_opcache_huge_code_pages__host_var: 0
php__ini_opcache_interned_strings_buffer__host_var: 12
php__ini_opcache_max_accelerated_files__host_var: 7963
php__ini_opcache_memory_consumption__host_var: 128
php__ini_opcache_revalidate_freq__host_var: 60
php__ini_opcache_save_comments__host_var: 1
php__ini_opcache_validate_timestamps__host_var: 1
php__ini_post_max_size__host_var: '8M'
php__ini_session_cookie_httponly__host_var: 'On'
php__ini_session_cookie_samesite__host_var: 'Lax'
php__ini_session_cookie_secure__host_var: 'On'
php__ini_session_gc_maxlifetime__host_var: 1440
php__ini_session_sid_length__host_var: 32
php__ini_session_trans_sid_tags__host_var: 'a=href,area=href,frame=src,input=src,form=fakeentry'
php__ini_smtp__host_var: 'localhost'
php__ini_upload_max_filesize__host_var: '10000M'
Optional Role Variables - PHP-FPM Global Config Directives¶
Variables for the [global] section of the PHP-FPM configuration, deployed as z00-linuxfabrik-global.conf next to the pools. Only directives that no packaged php-fpm.conf sets itself can be configured here, see "How the Role Behaves".
php__fpm_conf_emergency_restart_interval__group_var / php__fpm_conf_emergency_restart_interval__host_var
- The window
php__fpm_conf_emergency_restart_threshold__*_varcounts within. Available units: s(econds), m(inutes), h(ours), or d(ays). A value of0switches the mechanism off. - Type: String.
- Default:
'1m' - Deviates from the upstream default
0: the mechanism needs a non-zero threshold and a non-zero interval, so leaving either at zero switches it off.
php__fpm_conf_emergency_restart_threshold__group_var / php__fpm_conf_emergency_restart_threshold__host_var
- Reload PHP-FPM once this many workers have died on
SIGSEGVorSIGBUSwithinphp__fpm_conf_emergency_restart_interval__*_var. A value of0means off. - Type: Number.
- Default:
10 - Deviates from the upstream default
0: an extension or opcode cache that corrupts its workers otherwise keeps crashing them until somebody notices, while a reload of the master usually restores service. Ten crashes in a minute is well clear of ordinary application fatals, which do not count here: onlySIGSEGVandSIGBUSdo. PHP-FPM writes a WARNING when it triggers, so the crash still reaches monitoring instead of being papered over.
php__fpm_conf_log_level__group_var / php__fpm_conf_log_level__host_var
- The log level of PHP-FPM's own error log. Possible values:
alert,error,warning,notice,debug. - Type: String.
- Default:
'notice' - Matches the upstream default but is pinned rather than left unset, because PHP-FPM keeps an unset value at zero internally and
php-fpm -ttthen dumpslog_level = unknown valueinstead of the level actually in effect. Raising it towarningdrops the start, reload and shutdown markers that make a pool restarting in a loop visible, and silences thephp-fpm -ttconfiguration dump along with them.
Example:
# optional
php__fpm_conf_emergency_restart_interval__host_var: '1m'
php__fpm_conf_emergency_restart_threshold__host_var: 10
php__fpm_conf_log_level__host_var: 'notice'
Optional Role Variables - PHP-FPM Pool Config Directives¶
Variables for PHP-FPM pool directives and their default values, defined and supported by this role.
php__fpm_pool_conf_pm__group_var / php__fpm_pool_conf_pm__host_var
- Choose how the process manager will control the number of child processes.
- Type: String.
- Default:
'dynamic'
php__fpm_pool_conf_pm_max_children__group_var / php__fpm_pool_conf_pm_max_children__host_var
- The number of child processes to be created when pm is set to 'static' and the maximum number of child processes when pm is set to 'dynamic' or 'ondemand'.
- Type: Number.
- Default:
50 - Deviates from the upstream default on Debian, which ships
5: the role uses one value for both families, and 5 workers serve a single application on a modern host badly. Note the ceiling this sets on memory: each worker may grow tophp__ini_memory_limit__*_var, and the value applies per pool, so a host with several pools multiplies it.
php__fpm_pool_conf_pm_max_spare_servers__group_var / php__fpm_pool_conf_pm_max_spare_servers__host_var
- The desired maximum number of idle server processes.
- Type: Number.
- Default:
35 - Deviates from the upstream default on Debian, which ships
3: the role uses the RedHat package value for both families. Idle workers are only reaped down to this number, so this is what a pool costs at rest.
php__fpm_pool_conf_pm_min_spare_servers__group_var / php__fpm_pool_conf_pm_min_spare_servers__host_var
- The desired minimum number of idle server processes.
- A pool that keeps falling below this logs
seems busyonce per second until it recovers, see "How the Role Behaves". - Type: Number.
- Default:
5 - Deviates from the upstream default on Debian, which ships
1: the role uses the RedHat package value for both families.
php__fpm_pool_conf_pm_start_servers__group_var / php__fpm_pool_conf_pm_start_servers__host_var
- The number of child processes created on startup. Must be greater than
php__fpm_pool_conf_pm_min_spare_servers__*_varbut less thanphp__fpm_pool_conf_pm_max_spare_servers__*_var. - Type: Number.
- Default:
5 - Deviates from the upstream default on Debian, which ships
2: the role uses the RedHat package value for both families.
php__fpm_pool_conf_request_slowlog_timeout__group_var / php__fpm_pool_conf_request_slowlog_timeout__host_var
- The timeout for serving a single request after which a PHP backtrace will be dumped to the slowlog file. A value of
0means off. Available units: s(econds, default), m(inutes), h(ours), or d(ays). The slowlog is written to the per-service log directory,/var/log/php-fpm/<pool>-slow.logon RedHat and/var/log/<service>/<pool>-slow.logon Debian, for example/var/log/php8.4-fpm/www-slow.log. On RedHat the backtrace also needs thelfops_php_fpm_slowlogSELinux module, see "How the Role Behaves". - Type: Number.
- Default:
0
php__fpm_pool_conf_request_terminate_timeout__group_var / php__fpm_pool_conf_request_terminate_timeout__host_var
- The timeout for serving a single request after which the worker process will be killed. This is the backstop for requests that
max_execution_timecannot stop, because the script is blocked in a system call (a database query, an outgoing HTTP request) rather than executing PHP. Without it such a worker occupies its slot until it returns on its own, which fills uppm.max_childrenunder load long after the web server or a reverse proxy in front of it gave up on the request. Keep it abovephp__ini_max_execution_time__*_var(default30), so a script still hits PHP's own limit first and gets a proper error and log entry, and above the web server's own timeout (apache_httpd__conf_timeout, default10). A value of0means off. Available units: s(econds, default), m(inutes), h(ours), or d(ays). - Type: String.
- Default:
'60s' - Deviates from the upstream default
0(off): without it a worker blocked in a system call occupies its slot until it returns on its own, long after the web server or a reverse proxy gave up on the request, which is howpm.max_childrenfills up under load.
php__fpm_pools__host_var / php__fpm_pools__group_var
- List of dictionaries containing PHP-FPM pools.
- For the usage in
host_vars/group_vars(can only be used in one group at a time). - Type: List of dictionaries.
- Default:
[] -
Subkeys:
-
name:- Mandatory. The name of the pool. Will also be used as the filename and for logfiles.
- Type: String.
-
state:- Optional. State of the pool. Possible options:
absent,present. - Type: String.
- Default:
'present'
- Optional. State of the pool. Possible options:
-
user:- Optional. The Unix user running the pool processes. php.net
- Type: String.
- Default:
'apache'(RedHat),'www-data'(Debian)
-
group:- Optional. The Unix group running the pool processes. php.net
- Type: String.
- Default:
'apache'(RedHat),'www-data'(Debian)
-
listen_acl_users:- Optional. The users granted access to the pool's socket, as a POSIX ACL. php.net
- Type: List of strings.
- Default: the web server user,
apache(RedHat) orwww-data(Debian) - Deviates from the upstream default on both families: the RedHat package grants
apache,nginx, and the Debian package hands the socket over throughlisten.owner/listen.groupinstead. The role grants only Apache HTTPd, since that is the web server currently provided by LFOps, and grants it by ACL on both, so socket access does not depend on who the pool runs as.
-
pm:- Optional. Choose how the process manager will control the number of child processes. php.net
- Type: String.
- Default:
{{ php__fpm_pool_conf_pm__combined_var }}(which defaults to'dynamic')
-
pm_max_children:- Optional. The number of child processes to be created when
pmis set to'static', and the maximum number of child processes whenpmis set to'dynamic'or'ondemand'. php.net - Type: Number.
- Default:
{{ php__fpm_pool_conf_pm_max_children__combined_var }}(which defaults to50)
- Optional. The number of child processes to be created when
-
pm_start_servers:- Optional. The number of child processes created on startup. Must be greater than
pm_min_spare_serversbut less thanpm_max_spare_servers. Used only whenpmis set to'dynamic'. php.net - Type: Number.
- Default:
{{ php__fpm_pool_conf_pm_start_servers__combined_var }}(which defaults to5)
- Optional. The number of child processes created on startup. Must be greater than
-
pm_min_spare_servers:- Optional. The desired minimum number of idle server processes. Used only when
pmis set to'dynamic'. php.net - Type: Number.
- Default:
{{ php__fpm_pool_conf_pm_min_spare_servers__combined_var }}(which defaults to5)
- Optional. The desired minimum number of idle server processes. Used only when
-
pm_max_spare_servers:- Optional. The desired maximum number of idle server processes. Used only when
pmis set to'dynamic'. Idle workers are only reaped down to this number, so on a host with several pools this is what they cost at rest. php.net - Type: Number.
- Default:
{{ php__fpm_pool_conf_pm_max_spare_servers__combined_var }}(which defaults to35)
- Optional. The desired maximum number of idle server processes. Used only when
-
pm_max_spawn_rate:- Optional. The number of child processes to spawn at once. Used only when
pmis set to'dynamic'. Only rendered on PHP 8.1 and newer, where the directive exists. php.net - Type: Number.
- Default:
32
- Optional. The number of child processes to spawn at once. Used only when
-
pm_process_idle_timeout:- Optional. The number of seconds after which an idle process will be killed. Used only when
pmis set to'ondemand'. Available units: s(econds, default), m(inutes), h(ours), or d(ays). php.net - Type: String.
- Default:
'10s'
- Optional. The number of seconds after which an idle process will be killed. Used only when
-
pm_max_requests:- Optional. The number of requests each child process should execute before respawning, which bounds the damage a leaking third-party library can do. For endless request processing specify
0. php.net - Type: Number.
- Default:
500 - Deviates from the upstream default
0(never respawn): a worker that leaks memory keeps it for the lifetime of the service, so a slow leak in an application or an extension eventually shows up as an out-of-memory kill rather than as a recycled worker.
- Optional. The number of requests each child process should execute before respawning, which bounds the damage a leaking third-party library can do. For endless request processing specify
-
pm_status_path:- Optional. Path to view the FPM status page. Set to an empty string to disable the status page for this pool. php.net
- Type: String.
- Default:
'/fpm-status' - Deviates from the upstream default, which sets no status path at all and therefore serves no status page: the php-fpm-status check of the Linuxfabrik Monitoring Plugins reads it, and defaults to exactly this path.
-
ping_path:- Optional. The ping path to check if FPM is alive and responding. Set to an empty string to disable the ping endpoint for this pool. php.net
- Type: String.
- Default:
'/fpm-ping' - Deviates from the upstream default, which sets no ping path at all: the php-fpm-ping check of the Linuxfabrik Monitoring Plugins reads it, and defaults to exactly this path.
-
request_slowlog_timeout:- Optional. The timeout for serving a single request after which a PHP backtrace will be dumped to the slowlog file. A value of
0means off. Available units: s(econds, default), m(inutes), h(ours), or d(ays). php.net - Type: Number.
- Default:
{{ php__fpm_pool_conf_request_slowlog_timeout__combined_var }}(which defaults to0)
- Optional. The timeout for serving a single request after which a PHP backtrace will be dumped to the slowlog file. A value of
-
request_slowlog_trace_depth:- Optional. Depth of the slowlog stack trace. php.net
- Type: Number.
- Default:
20
-
request_terminate_timeout:- Optional. The timeout for serving a single request after which the worker process will be killed. A value of
0means off. Available units: s(econds, default), m(inutes), h(ours), or d(ays). php.net - Type: String.
- Default:
{{ php__fpm_pool_conf_request_terminate_timeout__combined_var }}(which defaults to'60s')
- Optional. The timeout for serving a single request after which the worker process will be killed. A value of
-
php_admin_value_max_execution_time:- Optional. Enforced as
php_admin_value, so an application cannot raise it at runtime viaini_set(). php.net - Type: Number.
- Default:
{{ php__ini_max_execution_time__combined_var }} - Deviates from the upstream default in enforcement, not in value: upstream sets it in
php.inionly, where an application can raise it at runtime withini_set(). As aphp_admin_valueit is a ceiling the pool cannot exceed, which is what keeps one pool from taking the host down for the others. The CLI is unaffected, so cron jobs and maintenance scripts keep thephp.inivalue and their ownini_set().
- Optional. Enforced as
-
php_admin_value_max_input_vars:- Optional. Enforced as
php_admin_value. php.net - Type: Number.
- Default:
{{ php__ini_max_input_vars__combined_var }} - Deviates from the upstream default in enforcement, not in value: upstream sets it in
php.inionly, where an application can raise it at runtime withini_set(). As aphp_admin_valueit is a ceiling the pool cannot exceed, which is what keeps one pool from taking the host down for the others. The CLI is unaffected, so cron jobs and maintenance scripts keep thephp.inivalue and their ownini_set().
- Optional. Enforced as
-
php_admin_value_memory_limit:- Optional. Enforced as
php_admin_value. php.net - Type: String.
- Default:
'{{ php__ini_memory_limit__combined_var }}' - Deviates from the upstream default in enforcement, not in value: upstream sets it in
php.inionly, where an application can raise it at runtime withini_set(). As aphp_admin_valueit is a ceiling the pool cannot exceed, which is what keeps one pool from taking the host down for the others. The CLI is unaffected, so cron jobs and maintenance scripts keep thephp.inivalue and their ownini_set().
- Optional. Enforced as
-
php_admin_value_open_basedir:- Optional. Limits the files the pool may access to the given paths. php.net
- Type: String.
- Default: unset
-
php_admin_value_post_max_size:- Optional. Enforced as
php_admin_value. php.net - Type: String.
- Default:
'{{ php__ini_post_max_size__combined_var }}' - Deviates from the upstream default in enforcement, not in value: upstream sets it in
php.inionly, where an application can raise it at runtime withini_set(). As aphp_admin_valueit is a ceiling the pool cannot exceed, which is what keeps one pool from taking the host down for the others. The CLI is unaffected, so cron jobs and maintenance scripts keep thephp.inivalue and their ownini_set().
- Optional. Enforced as
-
php_admin_value_session_save_handler:- Optional. The session storage backend for this pool. Enforced as
php_admin_value, so an application cannot switch it at runtime viaini_set(). Set it toredisormemcached(with the matchingphp_admin_value_session_save_pathconnection string, and the extension installed viaphp__modules__*_var) on a host whose sessions have to survive beyond one machine, for example behind a load balancer. The role then creates no session directory for the pool. php.net - Type: String.
- Default:
'files' - Deviates from the upstream default in enforcement, not in value: the RedHat package sets it in the pool as
php_value, whichini_set()can still change, and the Debian package leaves it tophp.ini. As aphp_admin_valuean application cannot switch its session backend at runtime, which is what keeps a pool's sessions in the directory the role created for it. The CLI is unaffected.
- Optional. The session storage backend for this pool. Enforced as
-
php_admin_value_session_save_path:- Optional. With the default
fileshandler the role creates this directory, owned by the pool'suser/groupwith mode0700. On RedHat it inherits thehttpd_var_run_tSELinux type from the session base; pointing it outside that base means labeling it yourself. With anotherphp_admin_value_session_save_handlerthis is the backend's connection string (for exampletcp://192.0.2.10:6379) and no directory is created. php.net - Type: String.
- Default:
/var/lib/php/session/<pool>(RedHat),/var/lib/php/sessions/<pool>(Debian) - Deviates from the upstream default, which points every pool at the one shared session base (
/var/lib/php/sessionon RedHat as aphp_value,/var/lib/php/sessionsfromphp.inion Debian): pools sharing one directory can read each other's session files, and with it each other's logged-in users.
- Optional. With the default
-
php_value_session_cookie_httponly/php_value_session_cookie_samesite/php_value_session_cookie_secure:- Optional. The session cookie policy for this pool, overriding the host-wide
php__ini_session_cookie_*__*_varfor its own workers. Use them where one host serves applications with different needs: an application whose identity provider returns through a cross-site POST (SAML HTTP-POST binding, OIDCform_post) needsphp_value_session_cookie_samesite: 'None'together withphp_value_session_cookie_secure: 'On', which browsers require forNone, while the rest of the host keepsLax. An internal site served over plain HTTP needsphp_value_session_cookie_secure: 'Off'.session.cookie_samesiteis only rendered from PHP 7.3 on, since 7.2 does not know the directive. - Type: String.
- Default: the host-wide
php__ini_session_cookie_httponly__*_var,php__ini_session_cookie_samesite__*_varandphp__ini_session_cookie_secure__*_var - Deployed as
php_valueand not asphp_admin_value, unlike the limits above: those are ceilings a pool must not raise, whereas the cookie policy is something an application may legitimately manage itself throughsession_set_cookie_params(). As aphp_admin_valuethat call is refused (ini_set()returnsfalseand the value does not move), and an application that deliberately needsNonefails at runtime as a login loop rather than visibly.php_valuealso keeps the semantics thephp.inisetting already had.
- Optional. The session cookie policy for this pool, overriding the host-wide
-
php_admin_value_soap_wsdl_cache_dir:- Optional. Where the SOAP extension caches parsed WSDL files. The role creates this directory, owned by the pool's
user/groupwith mode0700. php.net - Type: String.
- Default:
/var/lib/php/wsdlcache/<pool> - Deviates from the upstream default
/tmp, which the RedHat package already overrides with the shared/var/lib/php/wsdlcache: a cached WSDL carries the internal endpoints and message types of the service it describes, and pools do not necessarily run as the same user, so each pool caches into its own directory.
- Optional. Where the SOAP extension caches parsed WSDL files. The role creates this directory, owned by the pool's
-
php_admin_value_upload_max_filesize:- Optional. Enforced as
php_admin_value. php.net - Type: String.
- Default:
'{{ php__ini_upload_max_filesize__combined_var }}' - Deviates from the upstream default in enforcement, not in value: upstream sets it in
php.inionly, where an application can raise it at runtime withini_set(). As aphp_admin_valueit is a ceiling the pool cannot exceed, which is what keeps one pool from taking the host down for the others. The CLI is unaffected, so cron jobs and maintenance scripts keep thephp.inivalue and their ownini_set().
- Optional. Enforced as
-
raw:- Optional. Raw content which will be added to the end of the pool config.
- Type: String.
- Default: unset
-
Example:
# optional
php__fpm_pool_conf_pm__host_var: 'dynamic'
php__fpm_pool_conf_pm_max_children__host_var: 50
php__fpm_pool_conf_pm_max_spare_servers__host_var: 35
php__fpm_pool_conf_pm_min_spare_servers__host_var: 5
php__fpm_pool_conf_pm_start_servers__host_var: 5
php__fpm_pool_conf_request_slowlog_timeout__host_var: '10s'
php__fpm_pool_conf_request_terminate_timeout__host_var: '60s'
php__fpm_pools__host_var:
- name: 'librenms'
user: 'librenms'
group: 'librenms'
pm: 'ondemand'
pm_max_children: 10
pm_process_idle_timeout: '60s'
php_admin_value_memory_limit: '256M'
php_admin_value_open_basedir: '/opt/librenms:/tmp'
request_terminate_timeout: '120s'
raw: |-
env[PATH] = /usr/local/bin:/usr/bin:/bin
Troubleshooting¶
The run aborts with request_terminate_timeout ... has to be greater than max_execution_time
- A pool would be configured so that PHP-FPM kills the worker before PHP reaches its own limit, which caps the longer runtime silently and replaces PHP's fatal error with a bare
execution timed out. Raisephp__fpm_pool_conf_request_terminate_timeout__*_varabovephp__ini_max_execution_time__*_var(leaving headroom, so PHP's limit is the one that trips), set it per pool via the pool'srequest_terminate_timeout, or lower the execution time. Setting either of the two to0switches that limit off and is accepted.
The run aborts with PHP X.Y is not supported by this role
- The enabled repositories offer a PHP version this role ships no ini template and vars file for. Either pin the host to a supported version via
php__version, or add the matchingroles/php/templates/etc/php.d/<version>-z00-linuxfabrik.ini.j2androles/php/vars/<version>.yml, and list the version inroles/php/vars/main.yml.