PHP-FPM integration released

This is part of the apnscp mainline documentation.

Refer to PHP-FPM.md for the most current documentation.

PHP-FPM is a high performance PHP daemon built on FastCGI and introduced in apnscp 3.1. On apnscp platforms PHP-FPM demonstrates a 2-3x higher throughput than mod_php (“ISAPI”), which integrates into Apache as a module. In PHP-FPM, a request is sent over a UNIX domain socket to a dedicated worker pool for processing. In ISAPI, PHP requests are handled by a separate VM integrated into Apache that must scaffold and tear down at the end of each request. ISAPI is ideal in low-memory environments but loses relevance outside this niche scenario.

PHP-FPM offers several advantages over an ISAPI integration:


Resource enforcement
PHP-FPM pools run under the group ID of each account, which affords simple cgroup treatment to each process. ISAPI runs in a threaded environment that is incompatible with cgroup v1 (cgroup v2 supports threaded accounting at the cost of immense complexity). Every request that comes through may be governed by CPU, memory, block IO, and network IO limits. Likewise every request may be accounted towards an account’s cumulative usage.

Pools run within the synthetic root of each account ensuring isolation between accounts. ISAPI uses a variety of mechanisms to impede arbitrary access (SECURITY.md) that may provide a loose deterrent. PHP-FPM requests are jailed using systemd namespaces, a powerful OS feature that is part of userland management (PID 1).

To limit snooping or the potential of socket remaps, sockets are stored outside the synthetic root in a general runtime directory that inhibits access beyond directory access using conventional discretionary access controls.

Path cache
Running each account in a jail obviates the requirement for open_basedir restrictions. open_basedir restrictions disable realpath caching (bug #52312) to stymie symlink attacks. Enabling realpath caches improves throughput by caching filesystem metadata and making assumptions about the properties of the file.

User customization
As each pool runs independent of other accounts, end-users may tailor the PHP pool to their needs, such as changing the process manager or PHP settings without relying on .htaccess directives that are always re-evaluated on each request. Users cannot tune critical values such as the pool user, chroot, socket path (without grave consequence) or other protected values as these are extracted to the systemd service definition.

cgroup enforcement is strongly encouraged to prevent abuse. Set cgroup,memory as a minimum to ensure that a user cannot define a static pool that could spawn an egregious number of workers to cause an out-of-memory condition.

RewriteBase fixup
SCRIPT_FILENAME is rewritten before being passed to proxy. Because of this, PHP scripts on a subdomain or addon domain no longer require RewriteBase / to be set greatly reducing confusion on migrating to an apnscp platform.

Multi-tenancy PENDING
Accounts may spawn multiple PHP-FPM pools each with different users. For example, it would be possible to create a PHP pool for production and staging in which the production environment adheres to the principles of Fortification and the staging environment is owned entirely by the developer account; both operate under different UIDs.

cgroup enforcement is strongly encouraged to prevent abuse. Set cgroup,memory as a minimum to ensure that a user cannot define a static pool that could spawn an egregious number of workers to cause an out-of-memory condition.

Flexible ownership
Similarly, each pool may operate under a different UID, preferably a system user without machine access, to provide further separation between accounts and ensure zero overlap when discretionary access controls are applied. Setting the pool to the account owner facilitates easy management without the need for Fortification but also  negates any benefits of audit trails should an account get hacked from an insecure WordPress plugin or any PHP application.

To place a FPM pool under the account user for ease of convenience,

EditDomain -c apache,webuser=adminuser -D domain.com
# To switch back to system user
EditDomain -c apache,webuser=apache -D domain.com


Memory requirements
Each pool requires a minimum of 40 MB. In real-world situations each additional worker may require an additional 16-24 MB memory in typical usage scenarios. Workers that spawn from the pool manager follow copy-on-write semantics idiosyncratic of a fork() syscall, meaning the address space for PHP is shared when a worker spawns. Only the PHP scripts loaded within the worker are allocated additional memory.

To help ameliorate memory constraints in high density environments, PHP-FPM uses the ondemand process manager to automatically sleep idle processes after a set time (1 minute). This can be overrode by changing the PHP-FPM configuration template. ondemand exhibits a very low latency when used in conjunction with OPCache (enabled by default) to spin up additional workers.

Switching an account over is a breeze! Flip the apache,jail setting to enable jailing:

EditDomain -c apache,jail=1 -D domain.com

To set the default going forward, either make the adjustment in a plan via ./artisan opcenter:plan or set the default FPM behavior, cpcmd config:set apnscp.config httpd use_fpm true. All new accounts created will use PHP-FPM by default.

To perform an en-masse edit:

cd /home/virtual
for i in site[0-9]* ; do
  EditDomain -c apache,jail=1 $i

And for the overachieving variety:

yum install -y jq
cpcmd -o json admin:collect null '[apache.jail:0]' | jq 'keys[]' | tr -d '"' | while read -r SITE ; do   
  echo "Editing $(get_config "$SITE" siteinfo domain)"
  EditDomain -c apache,jail=1 "$SITE"

There will be an elision delay configured in [httpd] => reload_delay designed to allow multiple HTTP reload calls to merge into a single call to prevent a denial of service attack. By default, this is 2 minutes.

Worker throughput may be examined via systemd. FPM workers are watchdog-aware, which means they automatically report health back to systemd within a deadline window to improve reliability, recovering as needed. Worker metrics may be examined via systemctl status,

systemctl status php-fpm-site1-domain.com

PHP-FPM workers are grouped <site>-<marker> . By default the marker is the primary domain on the account. site is the immutable siteXX designator of the domain.

systemctl status php-fpm-site1-domain.com

# Sample response follows
● php-fpm-site1-domain.com.service - PHP worker for site1 - domain.com
   Loaded: loaded (/usr/local/apnscp/resources/templates/apache/php/fpm-service.blade.php; disabled; vendor preset: disabled)
   Active: active (running) since Fri 2019-08-30 17:35:01 EDT; 1min 25s ago
  Process: 17905 ExecStartPost=/bin/sh -c for i in /sys/fs/cgroup/*/site1/tasks ; do echo $MAINPID > $i ; done (code=exited, status=0/SUCCESS)
 Main PID: 17898 (php-fpm)
   Status: "Processes active: 0, idle: 1, Requests: 3, slow: 0, Traffic: 0.0667req/sec"
    Tasks: 1
   Memory: 26.0M
   CGroup: /system.slice/php-fpm-site1-domain.com.service
           ├─17898 php-fpm: master process (/etc/php-fpm.d/sites/domain.com.conf)
           └─17906 php-fpm: pool domain.com

apnscp manages pool groups, restarting as needed after the elision window expires. To restart or suspend the pool for a site, use the php-fpm-siteXX service wrapper.

# Suspend all pools allocated to site1
# Note: socket activation will start the worker on demand!
systemctl stop php-fpm-site1
# Restart all PHP-FPM pools, for example configuration updated
systemctl restart php-fpm-site1

Permanent suspension may be achieved by disabling the corresponding socket,

systemctl mask php-fpm-site1-*.socket
systemctl stop php-fpm-site1-*

However this is seldom useful as suspending the account achieves a similar result:

SuspendDomain site1

All cgroup service directives apply to PHP-FPM workers, including blkio IO throttling. To set a 2 MB/s write throttle on all PHP-FPM tasks use blkio,writebw or throttle IOPS use the “iops” equivalent, blkio,writeiops:

EditDomain -c cgroup,writebw=2 domain.com
# Apply the min of blkio,writ.ebw/blkio,writeiops
# Both are equivalent assuming 4 KB blocks
EditDomain -c cgroup,writebw=2 -c blkio,writeiops=512 domain.com

Memory ceilings likewise may be set via cgroup,memory.

# Set ceiling of 512 MB for all processes
EditDomain -c cgroup,memory=512 domain.com

IO and CPU weighting may be set via ioweight and cpuweight respectively. ioweight requires usage of the CFQ/BFQ IO elevators.

# Default weight is 100
# Halve IO priority, double CPU priority
EditDomain -c cgroup,ioweight=50 -c cgroup,cpuweight=200 domain.com

You may also like


DKIM released

DKIM (“DomainKeys Identified Mail“) support has been added in the latest nightly build of ApisCP ...

Leave a reply

Your email address will not be published. Required fields are marked *

More in:ApisCP


cPanel migration guide

🛎️ This is outdated! See docs/admin/Migrations.md for the most recent version. apnscp now supports cPanel ...

apnscp 3.1 released

apnscp (a/k/a “ApisCP”) 3.1 has been released! Codenamed “Business as Usual”, 3.1 shifts focus back ...