PHP-FPM Monitoring with OpenTelemetry: Metrics, Alerts, and Dashboards
PHP-FPM (FastCGI Process Manager) is the standard way to run PHP applications in production, but without proper monitoring you are blind to worker pool exhaustion, rising queue depths, and slow requests. This guide shows you how to collect PHP-FPM metrics with the php-fpm_exporter, ship them through OpenTelemetry Collector, and visualize them in Uptrace.
The data flows through the following pipeline:
- PHP-FPM exposes a status endpoint with pool statistics.
- php-fpm_exporter reads the status endpoint and serves metrics in Prometheus format.
- OpenTelemetry Collector scrapes the exporter and forwards metrics to Uptrace.
- Uptrace stores, visualizes, and alerts on the metrics.
Why Monitor PHP-FPM?
PHP-FPM manages a pool of worker processes that handle incoming PHP requests. Without visibility into pool utilization, you cannot tell whether your application is dropping requests, running out of workers, or leaking memory until users complain.
Monitoring PHP-FPM helps you:
- Prevent outages by catching worker pool exhaustion before requests start queuing.
- Right-size your pools by observing idle vs. active process ratios.
- Detect memory leaks by tracking per-process memory consumption over time.
- Find slow endpoints by correlating slow request counts with application traces.
Prerequisites
Before you begin, make sure you have the following:
- PHP-FPM installed and running (PHP 7.x or 8.x).
- Access to PHP-FPM pool configuration (typically
/etc/php/8.x/fpm/pool.d/www.conf). - Docker (recommended) or a Linux host for running the exporter and collector.
- An Uptrace account (cloud or self-hosted) with a project DSN.
Step 1: Enable the PHP-FPM Status Endpoint
PHP-FPM exposes pool statistics through a built-in status page, but it is disabled by default. Enable it in your pool configuration file.
Edit your pool config (for example /etc/php/8.2/fpm/pool.d/www.conf):
; Enable the status endpoint
pm.status_path = /status
; Optionally listen on a TCP socket instead of a Unix socket
; so the exporter can reach it from another container
listen = 0.0.0.0:9000
Restart PHP-FPM to apply the changes:
sudo systemctl restart php8.2-fpm
Verify the status endpoint is working:
# Using cgi-fcgi (install with: apt-get install libfcgi-bin)
SCRIPT_NAME=/status \
SCRIPT_FILENAME=/status \
REQUEST_METHOD=GET \
cgi-fcgi -bind -connect 127.0.0.1:9000
You should see output containing fields like active processes, idle processes, and listen queue.
Step 2: Install php-fpm_exporter
php-fpm_exporter is a Prometheus exporter that reads the PHP-FPM status endpoint and exposes the data as Prometheus metrics on port 9253.
Docker (recommended)
docker run -d \
--name php-fpm-exporter \
--network host \
hipages/php-fpm_exporter:2 \
--phpfpm.scrape-uri tcp://127.0.0.1:9000/status
Binary
Download the latest release from GitHub and run it directly:
# Download (adjust version and architecture as needed)
curl -L -o php-fpm_exporter \
https://github.com/hipages/php-fpm_exporter/releases/download/v2.2.0/php-fpm_exporter_2.2.0_linux_amd64
chmod +x php-fpm_exporter
# Start the exporter as an HTTP server on :9253
./php-fpm_exporter server --phpfpm.scrape-uri tcp://127.0.0.1:9000/status
Verify the exporter is working by fetching its metrics endpoint:
curl http://localhost:9253/metrics | grep phpfpm
Step 3: Configure OpenTelemetry Collector
OpenTelemetry Collector scrapes the Prometheus endpoint exposed by the exporter and forwards the metrics to Uptrace.
Create a file called otel-collector-config.yaml:
receivers:
otlp:
protocols:
grpc:
http:
prometheus/phpfpm:
config:
scrape_configs:
- job_name: php-fpm
scrape_interval: 15s
static_configs:
- targets: ['php-fpm-exporter:9253']
processors:
resourcedetection:
detectors: [env, system]
resource:
attributes:
- key: service.name
value: php-fpm
action: upsert
- key: deployment.environment
value: production
action: upsert
cumulativetodelta:
batch:
timeout: 10s
exporters:
otlp/uptrace:
endpoint: https://api.uptrace.dev:4317
headers:
uptrace-dsn: '<UPTRACE_DSN>'
service:
pipelines:
traces:
receivers: [otlp]
processors: [batch]
exporters: [otlp/uptrace]
metrics:
receivers: [otlp, prometheus/phpfpm]
processors: [cumulativetodelta, batch, resource, resourcedetection]
exporters: [otlp/uptrace]
telemetry:
logs:
level: 'info'
Replace <UPTRACE_DSN> with the DSN from your Uptrace project settings.
Step 4: Run Everything with Docker Compose
The easiest way to run the full stack is with Docker Compose. Create a docker-compose.yml:
version: '3.8'
services:
php-fpm:
image: php:8.2-fpm
volumes:
- ./app:/var/www/html
- ./php-fpm-pool.conf:/usr/local/etc/php-fpm.d/www.conf
ports:
- '9000:9000'
php-fpm-exporter:
image: hipages/php-fpm_exporter:2
command:
- '--phpfpm.scrape-uri=tcp://php-fpm:9000/status'
ports:
- '9253:9253'
depends_on:
- php-fpm
otel-collector:
image: otel/opentelemetry-collector-contrib:latest
command: ['--config=/etc/otel-collector-config.yaml']
volumes:
- ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
ports:
- '4317:4317'
- '4318:4318'
depends_on:
- php-fpm-exporter
Create a minimal pool configuration file called php-fpm-pool.conf:
[www]
user = www-data
group = www-data
listen = 0.0.0.0:9000
pm = dynamic
pm.max_children = 50
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 15
; Enable status endpoint
pm.status_path = /status
; Enable slow request logging
request_slowlog_timeout = 5s
slowlog = /proc/self/fd/2
Start the stack:
docker compose up -d
Available Metrics
php-fpm_exporter exposes these key metrics:
| Metric | Type | Description |
|---|---|---|
phpfpm_up | Gauge | PHP-FPM pool status (1 = up, 0 = down) |
phpfpm_active_processes | Gauge | Workers currently processing requests |
phpfpm_idle_processes | Gauge | Workers available and waiting for requests |
phpfpm_total_processes | Gauge | Total number of worker processes |
phpfpm_max_children_reached | Counter | Number of times the worker limit was hit |
phpfpm_slow_requests | Counter | Requests exceeding the slow threshold |
phpfpm_accepted_connections | Counter | Total connections accepted by the pool |
phpfpm_listen_queue | Gauge | Requests waiting in the listen queue |
phpfpm_listen_queue_len | Gauge | Maximum allowed size of the listen queue |
phpfpm_max_active_processes | Gauge | Highest observed active process count |
phpfpm_max_listen_queue | Gauge | Highest observed listen queue length |
Use these metrics in Uptrace or Grafana alternatives to create dashboards and set up alerting.
Alerting on Key Metrics
After the metrics are flowing into Uptrace, set up alerts for the most critical conditions.
Worker Pool Exhaustion
Alert when PHP-FPM reaches its maximum number of child processes. This means new requests will be queued or rejected:
# Uptrace alert rule
name: PHP-FPM max children reached
metrics:
- phpfpm_max_children_reached as $max_children
query:
- delta($max_children) > 0
for: 5m
annotations:
description: >
PHP-FPM pool has reached max_children {{$max_children}} times
in the last 5 minutes. Consider increasing pm.max_children.
Listen Queue Growing
Alert when requests start piling up in the listen queue, which indicates workers cannot keep up:
# Uptrace alert rule
name: PHP-FPM listen queue too long
metrics:
- phpfpm_listen_queue as $queue
query:
- $queue > 10
for: 2m
annotations:
description: >
PHP-FPM listen queue is at {{$queue}}. Requests are waiting
for available workers. Scale workers or investigate slow requests.
PHP-FPM Down
Alert when the exporter can no longer reach the PHP-FPM status endpoint:
# Uptrace alert rule
name: PHP-FPM is down
metrics:
- phpfpm_up as $up
query:
- $up == 0
for: 1m
annotations:
description: PHP-FPM pool is not responding. Check the PHP-FPM service status.
Troubleshooting
Max Children Reached
If phpfpm_max_children_reached counter keeps increasing, all workers are busy and new requests are being queued.
Fix: Increase the worker pool size in your PHP-FPM pool configuration:
pm.max_children = 50
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 15
The right value for pm.max_children depends on available memory. A rough formula: (total RAM - RAM used by other services) / average PHP process size.
High Memory Usage
If individual PHP worker processes grow over time, you likely have a memory leak in application code.
Fix: Recycle workers after a set number of requests:
pm.max_requests = 500
This causes each worker to exit and be replaced after handling 500 requests, releasing any leaked memory.
Slow Requests
Enable slow request logging to identify endpoints or code paths that are taking too long:
request_slowlog_timeout = 5s
slowlog = /var/log/php-fpm/slow.log
Review the slow log to find stack traces of requests that exceeded the threshold.
Exporter Cannot Connect to PHP-FPM
If the exporter logs show connection errors:
- Verify PHP-FPM is listening on the expected address:
shell
ss -tlnp | grep 9000 - Check the status endpoint is enabled by looking for
pm.status_pathin your pool config. - Check network connectivity between the exporter and PHP-FPM. If running in Docker, make sure they are on the same network.
No Metrics in Uptrace
If the collector is running but metrics are not showing up in Uptrace:
- Check the exporter is serving metrics:shell
curl http://localhost:9253/metrics - Check the collector logs for scrape errors:
shell
docker logs otel-collector 2>&1 | grep -i error - Verify the Uptrace DSN is correct in the collector config.
- Check network connectivity from the collector to
api.uptrace.dev:4317.
Collector Scrape Failures
If you see server returned HTTP status 403 Forbidden in the collector logs, the exporter cannot reach the PHP-FPM status endpoint. Double-check pm.status_path in your pool config and restart PHP-FPM.
What is Uptrace?
Uptrace is an OpenTelemetry APM that supports distributed tracing, metrics, and logs. You can use it to monitor applications and troubleshoot issues.

Uptrace comes with an intuitive query builder, rich dashboards, alerting rules with notifications, and integrations for most languages and frameworks.
Uptrace can process billions of spans and metrics on a single server and allows you to monitor your applications at 10x lower cost.
In just a few minutes, you can try Uptrace by visiting the cloud demo (no login required) or running it locally with Docker. The source code is available on GitHub.
What's Next?
With PHP-FPM metrics flowing into Uptrace, you can build dashboards that show pool utilization at a glance and alerts that fire before your application starts dropping requests.
To extend your PHP observability further, explore these related guides:
- OpenTelemetry PHP tracing - Instrument PHP application code with distributed traces
- Laravel monitoring - Monitor Laravel applications with auto-instrumentation
- Symfony monitoring - Symfony observability with OpenTelemetry
- MySQL monitoring - Track database query performance
- OpenTelemetry Collector - Learn more about collector configuration and processors
- Distributed tracing tools - Compare observability backends