//------------------------------------------------------------------- //-------------------------------------------------------------------
PHP-FPM Max Children Parameter

PHP-FPM Max Children: Production Sizing Formula for High Traffic

PHP-FPM max children is the most essential parameter for preventing 502 errors, memory exhaustion, and queue saturation on production servers. In this guide, you will learn how to calculate the optimal value based on real memory usage, detect queue bottlenecks, and validate changes under production-level traffic.

If you need a high-performance and reliable dedicated server to run PHP-FPM tuning in production, visit PerLod Hosting and choose a dedicated plan that matches your traffic and RAM needs.

What is the PHP-FPM Max Children Directive?

The pm.max_children directive defines the maximum number of simultaneous PHP worker processes that can exist at any given time. Each process handles one request at a time in PHP-FPM’s process-based model.

When all the available PHP-FPM workers are busy, new requests have to wait in a queue.

If that queue gets full, NGINX may return 502 Bad Gateway because it can’t hand the request to PHP-FPM. If pm.max_children is too low, traffic spikes cause slowdowns and dropped or failed requests; if it’s too high, PHP-FPM can use too much RAM, leading to out-of-memory (OOM) crashes and overall server instability.

PHP-FPM offers three process manager types controlled by the pm directive, including:

  • pm = dynamic: PHP-FPM automatically adds or removes worker processes depending on traffic, keeping at least pm.min_spare_servers is idle and never going above pm.max_children. This mode works well when traffic goes up and down, and you want good performance without using too much RAM.
  • pm = static: In this mode, PHP-FPM keeps a fixed number of worker processes running all the time. This means performance is consistent because it doesn’t need to create new workers, but it can waste RAM when traffic is low. It’s usually best for dedicated servers with steady high traffic and enough memory available.
  • pm = ondemand: In this mode, PHP-FPM starts worker processes only when a request comes in, and it stops them after they’ve been idle for pm.process_idle_timeout. This saves RAM, but the first requests can be slightly slower because PHP-FPM must create new workers. It’s a good fit for low-traffic sites or shared hosting environments.

Note: For high-traffic production environments, pm = dynamic offers the best balance, while pm = static delivers maximum throughput when resources permit.

Key configuration parameters include:

pm = dynamic
pm.max_children = 50
pm.start_servers = 12
pm.min_spare_servers = 8
pm.max_spare_servers = 16 
pm.max_requests = 500
  • pm.max_children: Max workers, which prevents RAM overload.
  • pm.start_servers: Workers started at launch.
  • pm.min_spare_servers: Minimum idle workers.
  • pm.max_spare_servers: Maximum idle workers, which avoid waste.
  • pm.max_requests: Recycle a worker after N requests, which limits memory growth.

PHP-FPM max_children memory Calculation

At this point, you can set how many PHP worker processes your server can run without running out of RAM. Instead of guessing, you can measure the real memory usage of one PHP-FPM process under normal load, then divide the RAM you can safely dedicate to PHP-FPM by that per-process number.

This gives a safe pm.max_children value that helps avoid 502 and 504 errors from overload and prevents OOM crashes from over-allocating workers.

Step 1. Measure Real Process Memory Usage

The best way to set pm.max_children is to measure how much RAM a real PHP-FPM worker uses on your live server or a staging server that’s the same.

Method 1: Check RSS memory per process with the command below:

ps -ylC php-fpm --sort:rss

In your output, you must see PHP-FPM processes sorted by RSS (Resident Set Size), which is real physical memory usage in kilobytes. Example output:

S   UID     PID    PPID  C PRI  NI   RSS    SZ WCHAN  TTY          TIME CMD
S    33   12345   12340  0  80   0 51200 87456 -      ?        00:00:03 php-fpm
S    33   12346   12340  0  80   0 48640 85120 -      ?        00:00:02 php-fpm
S    33   12347   12340  0  80   0 53760 89600 -      ?        00:00:05 php-fpm

Look at the RSS column, in this example, processes use about 50MB, 47MB, and 52MB.

Method 2: Calculate average memory per process with the command below:

ps --no-headers -o "rss,cmd" -C php-fpm | awk '{ sum+=$1 } END { printf ("%d%s\n", sum/NR/1024,"M") }'

This command adds up the RSS used by all PHP-FPM processes, then divides by how many processes there are, so you get the average memory per process in MB. For example, 52M.

Important Note: Measure PHP-FPM memory usage while the site is under real traffic, because each worker’s RAM usage depends heavily on your app, plugins, and themes. A common real-world baseline is around ~60 MB per PHP-FPM process in some environments, but it can be higher or lower depending on the workload.

Step 2. Determine Available RAM for PHP-FPM

You cannot allocate 100% of system RAM to PHP-FPM; you must reserve memory for:

  • Operating system: Around 500MB to 1GB, depending on distro.
  • NGINX web server: Typically 50 to 200MB.
  • Database server like MySQL or MariaDB, if co-located: Around 1 to 4GB.
  • Caching layers like Redis or Memcached: 256MB to 2GB.
  • Buffer and safety margin: 10 to 15% of total RAM.

Example calculation for a 4GB RAM server:

Total RAM:                    4096 MB
- OS/system overhead:          512 MB
- NGINX:                       100 MB
- MySQL:                      1024 MB
- Redis cache:                 256 MB
- Safety buffer (10%):         410 MB
------------------------------------------
Available for PHP-FPM:        1794 MB (~1.8GB)

Step 3. Apply the PHP-FPM max_children Formula

Here is the formula to calculate the PHP-FPM max children:

pm.max_children = (Available RAM for PHP-FPM in MB) / (Average process size in MB)

For example:

pm.max_children = 1794 MB / 52 MB = 34.5 → Round down to 34

Note: Always round down so you keep a safety buffer for traffic spikes and other services. For example, if one worker uses ~52 MB, then 35 workers would use about 1,820 MB (35 × 52), which can push you past your safe RAM limit.

Once you measured max_children, you must set the related parameter values. Here is the calculation for them:

pm.start_servers      = 25% of max_children
pm.min_spare_servers  = 25% of max_children  
pm.max_spare_servers  = 75% of max_children

For example, for max_children 34, it looks like this:

pm = dynamic
pm.max_children = 34
pm.start_servers = 8
pm.min_spare_servers = 8
pm.max_spare_servers = 25
pm.max_requests = 500

Alternative formula for CPU-bound workloads with heavy computation and low I/O wait:

pm.start_servers      = CPU cores × 4
pm.min_spare_servers  = CPU cores × 2
pm.max_spare_servers  = CPU cores × 4

Here are the example calculations for common scenarios:

  • Small VPS with 2GB RAM and 2 vCPUs:
Available RAM: 1024 MB
Process size: 45 MB
max_children = 1024 / 45 = 22

Configuration:
pm.max_children = 22
pm.start_servers = 6
pm.min_spare_servers = 6
pm.max_spare_servers = 16
  • Medium server with 8GB RAM and 4 vCPUs:
Available RAM: 5120 MB
Process size: 70 MB
max_children = 5120 / 70 = 73

Configuration:
pm.max_children = 70
pm.start_servers = 18
pm.min_spare_servers = 18
pm.max_spare_servers = 52
  • Large dedicated server with 32GB RAM and 16 cores:
Available RAM: 24000 MB
Process size: 80 MB
max_children = 24000 / 80 = 300

Configuration:
pm.max_children = 280
pm.start_servers = 70
pm.min_spare_servers = 70
pm.max_spare_servers = 210

Detect PHP-FPM max_children Queue Issues

As we mentioned, when all max_children workers are busy, new requests are added to a listen queue. PHP-FPM accepts connections but cannot process them immediately.

The queue depth is controlled by the listen backlog and the system-level limit:

listen.backlog = 511 ; Default queue size
sysctl net.core.somaxconn
# Output: net.core.somaxconn = 4096
  • listen.backlog = 511: Sets how many incoming connections PHP-FPM can keep waiting in its own queue before workers accept them.
  • sysctl net.core.somaxconn: Shows the Linux kernel limit for the maximum allowed size of that waiting queue.

Queue saturation signs include:

  • You may see 502 Bad Gateway when the waiting queue gets full.
  • You may see a 504 Gateway Timeout when requests sit in the queue for too long.
  • The site might go up and down during traffic spikes.
  • Pages can load slowly even if CPU usage looks low.

Method 1: Enable PHP-FPM Status Page

One method to detect queue saturation is to enable the PHP-FPM status page. Open your pool configuration file, for example:

sudo nano /etc/php/8.1/fpm/pool.d/www.conf

And edit:

pm.status_path = /fpm-status

Then, configure Nginx to expose it:

location ~ ^/fpm-status$ {
    access_log off;
    allow 127.0.0.1;
    deny all;
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_pass unix:/run/php/php8.1-fpm.sock;
}

Restart PHP-FPM and query the status with the command below:

curl http://localhost/fpm-status

Essential metrics you must monitor include:

pool:                 www
process manager:      dynamic
start time:           08/Jan/2026:14:32:15 +0000
start since:          3845
accepted conn:        284392
listen queue:         0          ← Current queue depth (0 = good)
max listen queue:     47         ← Peak queue depth since start
listen queue len:     511        ← Queue capacity (backlog size)
idle processes:       12
active processes:     18
total processes:      30
max active processes: 30         ← Processes have hit max_children
max children reached: 4          ← Times max_children was reached
slow requests:        0

Warning signs:

  • If the listen queue is always above 0, requests are piling up because workers can’t handle the load fast enough.
  • If the max listen queue is close to the listen queue len, the queue is almost full and may overflow soon.
  • ​If max children reached is above 0, PHP-FPM is hitting the pm.max_children limit regularly.
  • If idle processes are 0, there are no free workers ready to take new requests.

Method 2: Monitor PHP-FPM Error Logs

Another method to detect queue saturation is to monitor PHP-FPM error logs with the command below:

tail -f /var/log/php8.1-fpm.log

Look for the following warning:

WARNING: [pool www] server reached pm.max_children setting (50), consider raising it

This means all 50 workers are busy, and new requests are queuing. If this appears frequently, increase max_children or optimize application code.

Method 3: Check System Queue Overflow

You can also check the system queue overflow to detect the queue saturation:

dmesg | grep -i "listen queue"

Or check dropped connections with:

netstat -s | grep -i "listen"

Example output:

193 times the listen queue of a socket overflowed
36 SYNs to LISTEN sockets dropped

Fix Queue Saturation Issues

Once you detect the queue saturation, you can use the following methods to fix the issue.

1. If RAM is available, increase the max_children.

If memory monitoring shows available RAM and queue warnings persist, increase the PHP-FPM max children:

# Before:
pm.max_children = 30

# After:
pm.max_children = 50

Remember to recalculate supporting parameters accordingly.

2. Increase the listen queue depth.

To buffer more connections during short traffic spikes, increase PHP-FPM’s socket backlog, but remember it can’t exceed the system limit:

listen.backlog = 4096

Also, ensure the system limit is sufficient:

sudo sysctl -w net.core.somaxconn=4096

Make it persistent in /etc/sysctl.conf file:

net.core.somaxconn = 4096

3. Optimize application code.

You can reduce the per-request execution time by:

  • Enabling opcode caching (OPcache).
  • Using object caching like Redis.
  • Optimizing database queries, such as adding indexes and reducing N+1 queries.
  • Implementing CDN for static assets.
  • Enabling HTTP keep-alive to reduce connection overhead.

4. Use pm = static model for high traffic.

For long periods of heavy traffic, use pm = static so PHP-FPM keeps a fixed number of workers always running and doesn’t waste time creating or killing processes.

pm = static
pm.max_children = 50
pm.max_requests = 1000

This keeps all 50 workers alive continuously, ready to serve requests immediately.

PHP-FPM max_children Tuning Steps

Here is a simple checklist to safely adjust how many PHP-FPM workers your server can run in production. The goal is to increase capacity without causing high memory usage, queue buildup, or 502 and 504 errors.

1. Back up Existing Configuration

Before making changes, it is recommended to back up the current configuration. For example:

sudo cp /etc/php/8.1/fpm/pool.d/www.conf /etc/php/8.1/fpm/pool.d/www.conf.backup

2. Calculate pm and Apply New Values

After you measured the PHP-FPM max children and parameters, edit the pool configuration, for example:

sudo nano /etc/php/8.1/fpm/pool.d/www.conf

Update the parameters based on your calculations:

pm = dynamic
pm.max_children = 34
pm.start_servers = 8
pm.min_spare_servers = 8
pm.max_spare_servers = 25
pm.max_requests = 500

; Enable monitoring
pm.status_path = /fpm-status
listen.backlog = 4096

; Optional: Slow request logging
slowlog = /var/log/php8.1-fpm-slow.log
request_slowlog_timeout = 5s

The options are explained in the above steps. The slowlog logging means:

  • slowlog: The file path where PHP-FPM writes details about slow requests.
  • request_slowlog_timeout=5: The time limit; if a request runs longer than this, PHP-FPM logs it to slowlog.

3. Validate and Apply PHP FPM Configuration

Before restarting, test the configuration for syntax errors with the command below:

sudo php-fpm8.1 -t

Example output:

NOTICE: configuration file /etc/php/8.1/fpm/php-fpm.conf test is successful

If errors appear, review the configuration file and fix syntax issues.

To apply changes, you can use the following methods:

Method 1: Reload configuration without dropping active connections, which is recommended for production:

sudo systemctl reload php8.1-fpm

Or send the USR2 signal directly:

sudo kill -USR2 $(cat /run/php/php8.1-fpm.pid)

This finishes processing current requests before applying new settings.

Method 2: Full restart

If reload doesn’t apply changes, for example, changing pm type, restart the service:

sudo systemctl restart php8.1-fpm

Verify the service started successfully:

sudo systemctl status php8.1-fpm

4. Monitor PHP-FPM Changes Under Production Traffic

At this point, you can validate the changes under real traffic. Before the load test, check the current process count with the following command:

ps aux | grep php-fpm | grep -v grep | wc -l

Monitor memory usage with the command below:

free -m

Check PHP-FPM status:

curl http://localhost/fpm-status

Now you can test with 1,000 requests and 50 concurrent:

ab -n 1000 -c 50 -k http://yourdomain.com/

Key metrics to watch from the output:

Concurrency Level: 50
Time taken for tests: 12.345 seconds
Complete requests: 1000
Failed requests: 0 ← Should be 0
Requests per second: 81.01 [#/sec]
Time per request: 617.23 [ms]

Note: If failed requests are higher than 0, it means 505 and 504 errors. You must check the PHP-FPM logs and the status page.

Also, you can load test with wrk. To install it, run the commands below:

git clone https://github.com/wg/wrk.git
cd wrk
make
sudo cp wrk /usr/local/bin/

Run a 30-second test with 12 threads and 400 connections:

wrk -t12 -c400 -d30s --latency http://yourdomain.com/

Example output:

Running 30s test @ http://yourdomain.com/
  12 threads and 400 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   145.32ms   23.45ms   1.02s    89.23%
    Req/Sec   231.45     45.23    450.00    78.32%
  Latency Distribution
     50%  142ms
     75%  158ms
     90%  178ms
     99%  245ms
  83245 requests in 30.03s, 215.34MB read
Requests/sec:   2771.23
Transfer/sec:      7.17MB

During the test, monitor PHP-FPM:

watch -n 1 'curl -s http://localhost/fpm-status | grep -E "(listen queue|active processes|max children)"'

You must watch for:

  • The listen queue should stay at 0 or a very low value.
  • active processes shouldn’t be stuck at max_children all the time.
  • max children reached shouldn’t keep increasing, meaning PHP-FPM isn’t regularly hitting its worker limit.

Monitor system resources with the command below:

htop

Check for:

  • Memory usage
  • CPU usage
  • No swapping

5. Analyze PHP-FPM Slow Requests

If slowlog is enabled, you can review slow-executing scripts with the command below:

tail -f /var/log/php8.1-fpm-slow.log

Example output:

[pool www] pid 12345
script_filename = /var/www/html/index.php
[0x00007f8b4c614100] getProductList() /var/www/html/includes/products.php:87
[0x00007f8b4c613f80] processRequest() /var/www/html/index.php:45

This shows getProductList() took longer than request_slowlog_timeout. Optimize this function to reduce execution time.

6. Monitor for PHP-FPM 502 and 504 Errors

You can check Nginx error logs with the command below:

tail -f /var/log/nginx/error.log

The common 502 error pattern looks like this:

upstream prematurely closed connection while reading response header from upstream

This happens because the PHP-FPM worker crashed or timed out. To fix this, check the PHP-FPM error log for crashes, and increase request_terminate_timeout.

Another pattern looks like this:

connect() to unix:/run/php/php8.1-fpm.sock failed (2: No such file or directory)

This happens because the PHP-FPM service is not running. To fix this, start PHP-FPM with:

sudo systemctl start php8.1-fpm

Here is another pattern:

connect() to unix:/run/php/php8.1-fpm.sock failed (11: Resource temporarily unavailable)

This happens because all workers are busy and listen queue is full. To fix this, increase max_children or optimize application code.

Note: After load testing, you can adjust parameters if needed:

  • If memory usage is well below the limit, increase max_children by 20% to handle more concurrent traffic.
  • If queue saturation persists: Increase max_children if RAM is available or optimize the application to reduce request duration.
  • If memory pressure occurs: Reduce max_children by 10 to 15% and focus on application optimization.
  • If slow requests are common, investigate with profiling tools and optimize bottlenecks.

FAQs

What happens if pm.max_children is too low?

When all workers are busy, requests will be rejected, and you may get 502 errors. If the queue overflows, some connections get dropped during traffic spikes.

What happens if pm.max_children is too high?

If you set it too high, the server can run out of RAM. Then Linux may kill PHP-FPM processes, causing downtime or even making the whole server unstable.

Should I use pm static model or pm dynamic model for production?

Use pm = dynamic for most production sites because it scales workers up and down with traffic and saves memory when traffic is low. Use pm = static only when traffic is constantly high and you have plenty of RAM, since it keeps a fixed number of workers running all the time for maximum performance.

Conclusion

Setting PHP-FPM max children correctly is key to keeping high-traffic sites stable. Don’t trust defaults or copy-paste configs; measure real memory usage, calculate a safe value, then test and monitor in production. For best results, tune PHP-FPM and also optimize the app to reduce PHP load.

The goal isn’t to increase the max_children number; it’s to find the safe spot where your server handles target traffic reliably without losing resources or queuing requests.

We hope you enjoy this guide. Subscribe to our X and Facebook channels to get the latest articles.

For further reading:

Monitor Linux Logs with OpenSearch and Filebeat

Best GPU Servers for Vision Model Training

Post Your Comment

PerLod delivers high-performance hosting with real-time support and unmatched reliability.

Contact us

Payment methods

payment gateway
Perlod Logo
Privacy Overview

This website uses cookies so that we can provide you with the best user experience possible. Cookie information is stored in your browser and performs functions such as recognising you when you return to our website and helping our team to understand which sections of the website you find most interesting and useful.