What does PHP-FPM's ping.path endpoint check, and why is it not a full application health check?
answer
- unset by default
- answers pong with 200
- answered by a pool worker
- no database, no application code
- pm.status_listen for a separate pool
basics
~20 sping.path makes a PHP-FPM pool answer that URI with a plain-text 'pong' (ping.response) and status 200. It proves the web server can reach the pool and a worker is free, but runs no application code, so it cannot detect a broken database or app.
solid answer
~50 sSetting `ping.path` (for example `/ping`; unset by default, must start with `/`) makes the pool answer that script name itself with `text/plain`, status 200 and the body from `ping.response` (default `pong`). No PHP file is executed. It tells a load balancer or monitor two things: the web server can reach this pool's socket, and a worker picked the request up. It cannot tell you that the application works — no framework boot, no database or cache call — and because a normal pool worker answers it, a saturated pool answers late or not at all, which can make an orchestrator restart a pool that was merely busy. Use it for liveness of the FPM tier, a separate application endpoint for readiness, and `pm.status_listen` (PHP 8.0+) when status and ping must answer while the main pool is saturated. Keep it off public routes.
code
ini · 8 lines[www]
ping.path = /ping
ping.response = pong
; answer ping and status from a separate hidden pool (PHP 8.0+)
pm.status_path = /status
pm.status_listen = 127.0.0.1:9001
; keep health-check noise out of the access log
access.suppress_path[] = /pinggo deeper
Recall that ping.path makes PHP-FPM answer a URL with pong, and that this only shows FPM is reachable, not that the app works.
Explain that a normal worker answers it without running PHP, what it proves about the socket and pool, and why a readiness check needs application code.
Avoid restart storms: use pm.status_listen, tolerant liveness timeouts, and status metrics for saturation rather than ping latency.
Define the health-check contract per tier — FPM liveness, application readiness — and how orchestration reacts to each.
## What ping.path does Each PHP-FPM pool can answer a tiny **ping** request without running any PHP script: | Directive | Default | Meaning | |---|---|---| | `ping.path` | not set | Script name that FPM answers itself; must start with `/` | | `ping.response` | `pong` | Body of the reply, sent as `text/plain` with status 200 | When a request arrives at the pool whose script name matches `ping.path`, the worker that accepted it writes the response directly and skips executing any PHP file. The sample configuration suggests uses such as graphing FPM availability, removing a server from a load-balancer group when it stops responding, and alerting an operations team. Like the status page, the ping URI is not a real file: the web server must route that path to the pool with the matching script name, and its location should be restricted to monitoring clients. The sample configuration also warns against giving it a `.php` extension, which could conflict with a real PHP file. ## What a successful ping proves 1. The web server can **connect** to the pool's socket — the path, permissions and FPM process are fine. 2. A **worker** of that pool accepted the connection and answered, so the pool is not completely stuck. That makes it a good **liveness** signal for the PHP-FPM tier. ## What it does not prove - **The application works.** No framework boots, no autoloader runs, no database, cache or queue is contacted. A pool whose code fatals on every page, or whose database is down, still answers `pong`. - **Configuration is correct for real scripts.** `SCRIPT_FILENAME` mistakes, `security.limit_extensions` rejections or file permissions are not exercised. - **Latency of real pages.** A ping takes microseconds even when checkout takes ten seconds. For those, a readiness endpoint in the application — a small PHP script that checks its dependencies — is the right check. ## The saturation trap The ping is answered by an ordinary worker of the pool. When every worker is busy, the ping request waits in the same socket queue as user traffic. A health checker with a short timeout then marks the pool as dead, and an orchestrator may **restart** it — killing every in-flight request of a pool that was only busy, and making the overload worse. Two mitigations: - **`pm.status_listen`** (PHP 8.0+): gives status requests their own small hidden pool on a separate address, so they are answered even when the main pool is saturated. The hidden pool is created with `ondemand` and two workers, and serves the status and ping URIs of the pool it belongs to. - **Generous liveness timeouts and thresholds**, so that busy is not mistaken for dead; saturation is better detected from the status page's queue and active-process figures. ## Ping versus status | | `ping.path` | `pm.status_path` | |---|---|---| | Answer | Fixed text (`pong`) | Pool counters, optionally per worker | | Use | Is the pool reachable and answering? | How busy is it, and why? | | Cost | Trivial | Small, but the full view lists request URIs | | Exposure risk | Low | Leaks request paths if public | Both are answered by FPM itself without running a PHP file, both need a web-server route to the pool, and both benefit from `pm.status_listen` when the main pool is busy. ## Keeping pings quiet Monitoring pings every few seconds can dominate the pool's access log. `access.suppress_path[] = /ping` removes them, with safeguards: the suppression is ignored for requests that are not GET or HEAD, carry a body or query parameters, or get a non-2xx status. ## A typical container setup ```ini [www] listen = 9000 ping.path = /ping ping.response = pong pm.status_path = /status pm.status_listen = 127.0.0.1:9001 access.suppress_path[] = /ping ``` A sidecar or local probe sends a FastCGI request for `/ping` to `127.0.0.1:9001`; the application's own `/health` page, served through the normal pool, handles readiness. The split keeps "is FPM alive?" separate from "can the application serve users?".
- The orchestrator restarts PHP-FPM during every traffic peak because the ping check times out. What is going on?The ping is answered by a regular pool worker, so when all workers are busy it waits in the same queue as user requests and misses the probe's timeout. The restart then kills in-flight requests. Serve ping and status through `pm.status_listen`, relax the liveness timeout, and detect overload from the status page instead.
- The ping returns pong but every page shows a database error. Is the ping broken?No. `ping.path` is answered by FPM without executing any PHP file, so it never touches the database. It proves only that the pool is reachable and a worker responded. Application health needs its own endpoint that runs PHP and checks dependencies.
saying these in an interview costs you the question
- A pong response proves the database is reachable
- ping.path executes a PHP script called ping
- The ping is answered by the master, so it works when all workers are busy
- ping.path is enabled by default
- A failed ping always means PHP-FPM has crashed