skip to content

What does PHP-FPM's request_terminate_timeout do to a long request, and how does it relate to PHP's max_execution_time?

level: middleimportance: should knowfreq 35%

answer

  1. default 0: off
  2. master kills the worker with SIGTERM
  3. web server sees a 502
  4. backstop when max_execution_time does not stop it
  5. track_finished covers post-response work

basics

~20 s

request_terminate_timeout makes the FPM master kill a worker whose request runs past the limit; the client usually gets a 502 and a replacement is forked. Off by default, it backstops PHP's max_execution_time, which raises an error inside the script instead.

solid answer

~50 s

`request_terminate_timeout` is a per-pool limit (default `0`, off; units `s`, `m`, `h`, `d`) enforced from **outside** PHP: the FPM master watches wall-clock time since the request was accepted, and when it is exceeded it logs `execution timed out (… sec), terminating` and sends the worker SIGTERM. The request ends without a complete response, so the web server typically returns 502; the worker's exit is logged and a replacement is forked. `max_execution_time` works **inside** PHP and ends the script with a fatal error and normal cleanup, but it does not always stop a script — the FPM sample configuration names this directive as the fallback for that case. By default the timeout stops applying once the script calls `fastcgi_finish_request()` or is running shutdown functions; `request_terminate_timeout_track_finished = yes` keeps it engaged. Set it above your slowest legitimate request and above `request_slowlog_timeout`.

code

ini · 11 lines
ini
[shop]
request_slowlog_timeout = 3s
slowlog = /var/log/php-fpm/$pool.slow.log
; hard backstop, above normal pages
request_terminate_timeout = 30s
; also cover work after fastcgi_finish_request()
request_terminate_timeout_track_finished = yes

[reports]
; long exports get their own pool and limit
request_terminate_timeout = 300s

go deeper

for a junior

Recall that request_terminate_timeout makes PHP-FPM kill a request that runs too long, and that it is off by default.

for a middle

Contrast it with max_execution_time: outside versus inside PHP, SIGTERM versus a fatal error, a 502 versus a PHP error page, no cleanup versus shutdown functions.

for a senior

Set it deliberately: above legitimate slow requests, near the web server's timeout, above the slowlog threshold, with track_finished for post-response work.

for a principal

Define timeout budgets across tiers so the web server, FPM and PHP limits fail in a known order with known user-facing results.

## Two limits at two levels PHP-FPM offers a time limit that sits **outside** the PHP engine: | | `request_terminate_timeout` | `max_execution_time` | |---|---|---| | Configured in | The FPM pool file | php.ini, pool `php_value`, `ini_set()` | | Enforced by | The FPM **master** process | The PHP engine inside the worker | | How it stops a request | Kills the worker process (SIGTERM) | Raises a fatal error in the script | | What the client sees | Usually 502 from the web server | Whatever PHP sends — often a 500 page | | Cleanup | None: the process is gone | Shutdown functions still run | | Default | `0` (off) | Set in php.ini | `max_execution_time` belongs to PHP's runtime configuration; what matters here is that it does not always stop a script, and the PHP-FPM sample configuration describes `request_terminate_timeout` as the option to use "when the 'max_execution_time' ini option does not stop script execution for some reason". ## What happens when it fires 1. The master measures time since the worker **accepted** the request. 2. Once it exceeds the limit, the master sends the worker **SIGTERM** and logs a WARNING: `[pool shop] child 8790, script '/srv/shop/public/index.php' (request: "POST /checkout/confirm") execution timed out (30.001204 sec), terminating` 3. The worker dies mid-request: an open database transaction is left for the database to roll back when the connection drops, half-written files stay half-written, and no `finally` block or shutdown function runs. 4. The master logs the child's exit — `exited on signal 15 (SIGTERM)`, at WARNING level — and forks a replacement so the pool stays at size. 5. The web server's FastCGI connection closes without a complete response, which it normally reports as **502 Bad Gateway**. ## The track_finished switch By default the timeout is **not** engaged once the application has called `fastcgi_finish_request()` or has finished and is running shutdown functions registered with `register_shutdown_function()`. So a hung post-response task can hold a worker indefinitely. Setting `request_terminate_timeout_track_finished = yes` (available since PHP 7.3) applies the limit unconditionally. ## Interaction with the slow log `request_slowlog_timeout` must not be greater than a non-zero `request_terminate_timeout` — FPM rejects that configuration — because a request would be killed before its backtrace could be logged. The useful layout is slow log first, termination later: for example 3 s to log, 30 s to kill. ## Choosing a value - **Above legitimate slow requests.** Exports and reports that take 60 s will be killed at 30 s; give them a separate pool with a higher limit instead of raising it for everyone. - **Aligned with the web server.** If the web server gives up at 60 s (a 504) and FPM kills at 300 s, the worker keeps running for four minutes after anyone is listening. Setting FPM's limit near the web server's timeout frees workers sooner. - **Not a substitute for fixing slowness.** Each kill is a user-facing error and a lost worker; the slow log shows what to fix. ## Related global settings Two settings in `php-fpm.conf` concern failing workers rather than slow ones: - `emergency_restart_threshold` and `emergency_restart_interval` (both `0`, off, by default): if that many workers exit on SIGSEGV or SIGBUS within the interval, FPM reloads itself. - `process_control_timeout` (default `0`): the time limit for child processes to wait for a reaction on signals from the master, relevant to graceful reloads. ## Where to see it happening - **FPM error log:** each kill is a pair of WARNING lines — `execution timed out (… sec), terminating` with the pool, PID, script and request, then the child's `exited on signal 15 (SIGTERM)` line. - **Web server log:** a 502 at the same moment, with an error about the upstream closing the connection before a complete response. - **Status page:** nothing specific, but `active processes` drops by one and the replacement worker appears. Counting the terminating lines per hour is a simple alert: any sustained rate means users are getting errors. ## Summary `max_execution_time` is PHP's polite limit — an error inside the script. `request_terminate_timeout` is FPM's hard limit — the process is killed from outside. Production pools usually set both, with the FPM limit comfortably above the PHP one so that it only fires when the polite limit fails.

  • A request killed by request_terminate_timeout did not run its finally block or shutdown function. Why?
    The FPM master ends the worker with SIGTERM from outside the engine, so no PHP code runs afterwards: no `finally`, destructors or shutdown functions. That differs from PHP's own time limit, which raises a fatal error inside the script, after which shutdown functions still run.
  • A worker stays busy for minutes after calling fastcgi_finish_request(), despite request_terminate_timeout = 30s. Why?
    By default the terminate timeout is not engaged after `fastcgi_finish_request()` or while shutdown functions run. Set `request_terminate_timeout_track_finished = yes` so the limit applies to the whole lifetime of the request, including post-response work.

saying these in an interview costs you the question

  • request_terminate_timeout raises a catchable error inside the script
  • Shutdown functions still run after the FPM master kills the worker
  • request_terminate_timeout is enabled by default at 30 seconds
  • The killed worker leaves the pool one process short
  • It always applies after fastcgi_finish_request()