PHP-FPM Monitoring with OpenTelemetry: Metrics, Alerts, and Dashboards

Vladimir Mihailenco
November 19, 2025
7 min read

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:

  1. PHP-FPM exposes a status endpoint with pool statistics.
  2. php-fpm_exporter reads the status endpoint and serves metrics in Prometheus format.
  3. OpenTelemetry Collector scrapes the exporter and forwards metrics to Uptrace.
  4. 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):

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:

shell
sudo systemctl restart php8.2-fpm

Verify the status endpoint is working:

shell
# 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.

shell
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:

shell
# 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:

shell
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:

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:

yaml
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:

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:

shell
docker compose up -d

Available Metrics

php-fpm_exporter exposes these key metrics:

MetricTypeDescription
phpfpm_upGaugePHP-FPM pool status (1 = up, 0 = down)
phpfpm_active_processesGaugeWorkers currently processing requests
phpfpm_idle_processesGaugeWorkers available and waiting for requests
phpfpm_total_processesGaugeTotal number of worker processes
phpfpm_max_children_reachedCounterNumber of times the worker limit was hit
phpfpm_slow_requestsCounterRequests exceeding the slow threshold
phpfpm_accepted_connectionsCounterTotal connections accepted by the pool
phpfpm_listen_queueGaugeRequests waiting in the listen queue
phpfpm_listen_queue_lenGaugeMaximum allowed size of the listen queue
phpfpm_max_active_processesGaugeHighest observed active process count
phpfpm_max_listen_queueGaugeHighest 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:

yaml
# 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:

yaml
# 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:

yaml
# 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:

conf
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:

conf
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:

conf
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:

  1. Verify PHP-FPM is listening on the expected address:
    shell
    ss -tlnp | grep 9000
    
  2. Check the status endpoint is enabled by looking for pm.status_path in your pool config.
  3. 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:

  1. Check the exporter is serving metrics:
    shell
    curl http://localhost:9253/metrics
    
  2. Check the collector logs for scrape errors:
    shell
    docker logs otel-collector 2>&1 | grep -i error
    
  3. Verify the Uptrace DSN is correct in the collector config.
  4. 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 Overview

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: