PHP-Sysload is a native PHP 5 extension that checks the Linux system load at the beginning of every request. When one of three configured thresholds is exceeded, it replaces a response header and can terminate request startup before application code runs. This makes the module suitable for signaling overload to a reverse proxy or load balancer without adding the check to each PHP application.
The source code is available from the PHP-Sysload repository. Version 1.0.0 uses the PHP 5 Zend and SAPI extension APIs and reads /proc/loadavg directly. It can be built for compatible PHP environments on Linux; another PHP generation or operating system requires the corresponding API and load-source adaptations.
Request Lifecycle
PHP-Sysload exports no userland functions. Module initialization registers five INI directives, and PHP_RINIT performs the overload check before the PHP script begins. It opens /proc/loadavg, parses the first three whitespace-separated numbers, and compares them with the configured limits.
The Linux values are the 1-, 5-, and 15-minute load averages. The directive named sysload.ten-minutes is therefore a historical naming error: it controls the 15-minute value, not a 10-minute average. The extension triggers when any value is strictly greater than its threshold:
overloaded = load_1m > limit_1m
|| load_5m > limit_5m
|| load_15m > limit_15m Equality does not trigger the response. Load average measures demand for runnable or uninterruptible work, not CPU utilization as a percentage. A useful threshold depends on available processors, workload, queueing behavior, and the period for which overload is acceptable. The defaults are examples rather than universal capacity limits.
Configuration
Version 1.0.0 declares all directives with PHP_INI_ALL. Because the check occurs in PHP_RINIT, application code cannot use ini_set() to change the decision already made for the current request. Configure the values in the applicable system, pool, virtual-host, or per-directory PHP configuration before request initialization.
| Directive | Default | Runtime meaning |
|---|---|---|
sysload.one-minute | 1.0 | Threshold for the 1-minute Linux load average. |
sysload.five-minutes | 0.5 | Threshold for the 5-minute Linux load average. |
sysload.ten-minutes | 0.3 | Threshold for the 15-minute value despite the directive name. |
sysload.header | X-Sysload: Critical | Header line installed with SAPI replace semantics. |
sysload.force-exit | 1 | Flushes headers and aborts request startup after adding a nonempty overload header. |
extension=sysload.so
sysload.one-minute=4.0
sysload.five-minutes=3.0
sysload.ten-minutes=2.0
sysload.header="X-Sysload: Critical"
sysload.force-exit=1 The response action has an important dependency: the implementation evaluates force-exit only inside the branch for a nonempty sysload.header. Setting the header to an empty string therefore disables both the header and the forced exit. With a nonempty header and force-exit=0, application execution continues after the header is set. With force-exit=1, the extension calls php_header() and then zend_bailout().
Reverse-Proxy Contract
A proxy can inspect the configured header and stop assigning new work to an overloaded backend, select another upstream, or return a controlled overload response. The default X-Sysload header does not change the HTTP status, retry delay, or response body. The header setting accepts a complete SAPI header line, however, so it can instead be configured as Status: 503 Service Unavailable when the PHP SAPI recognizes that status form:
sysload.header="Status: 503 Service Unavailable"
sysload.force-exit=1 A dedicated overload header leaves routing policy to the proxy, while a status line gives clients an immediate failure response. In either case, retry behavior and upstream selection still belong to the surrounding proxy or client configuration.
The load check is host-wide. Every PHP worker reading the same /proc/loadavg observes the same machine averages, even when workers serve different applications. In containers, the visible load values depend on the host and procfs setup and may not correspond to the container's CPU quota. Thresholds should be validated in the actual deployment topology rather than copied from a bare-metal host.
Source Correction Required
Version 1.0.0 declares the parse-loop counter i in PHP_RINIT but does not initialize it before using i < 3 and indexing load[i]. Reading an uninitialized automatic variable is undefined behavior in C. The original source can skip parsing, write outside the three-element array, or compare values that were never assigned. A deployment build must initialize both the counter and the load array and verify that exactly three values were parsed before comparing thresholds.
float load[3] = {0.0f, 0.0f, 0.0f};
int i = 0;
/* Parse at most three values, then continue only when i == 3. */ The read path should also close the file when fgets() fails. A robust port should avoid strtok() and atof() for validation, reject malformed or non-finite values explicitly, and define whether a missing load source fails open or fails closed. These changes preserve the extension's request contract while removing undefined behavior from its decision path.
Building the Extension
The actual shared-extension switch in config.m4 is --enable-sysload:
git clone https://github.com/infusion/PHP-Sysload.git
cd PHP-Sysload
phpize
./configure --enable-sysload --with-php-config="$(command -v php-config)"
make
sudo make install Enable the resulting module in the applicable PHP configuration, add the five directives, and restart the relevant PHP workers. Verify the loaded module and version through php --ri sysload. The module information table reports sysload support as enabled and version 1.0.0.
The repository's older INSTALL text mentions ./configure without the explicit switch and uses --with-sysload for an in-tree build. The checked-in config.m4 declares PHP_ARG_ENABLE, so --enable-sysload is the source-of-truth option for both shared and in-tree configurations.
Porting PHP-Sysload
A port to another PHP extension API needs to update the function table type, module macros, thread-safety macros, INI access, SAPI header construction, and request-abort mechanism. The Linux input path can remain /proc/loadavg, or it can be replaced by a platform abstraction with equivalent 1-, 5-, and 15-minute values.
The port should add automated tests around threshold equality, each individual time window, malformed procfs input, an empty header, both force-exit modes, header replacement, and read failure. It should also decide whether thresholds remain PHP_INI_ALL or become system-only settings to make the overload policy immutable from request configuration.
References
- [Source]PHP-Sysload source repository, version 1.0.0.
- [Linux]Linux kernel documentation: The /proc filesystem.
- [phpize]PHP Manual: Compiling shared extensions with phpize.