Introduction to PHP-FPM Optimization
PHP-FPM (FastCGI Process Manager) tuning is a critical aspect of server optimization that involves
setting the correct process manager mode and, most importantly, the correct pm.max_children
value. Proper configuration ensures optimal server performance, prevents crashes, and maximizes resource
utilization.
⚠️ Critical Warning: Incorrect pm.max_children values can lead to server
crashes or poor performance. Too high values may cause memory exhaustion, while too low values result in
request queuing and slow response times.
When to Retune PHP-FPM
- When adding an additional WordPress site to your server
- After completing WordPress core, theme, or plugin updates
- When installing new themes or plugins
- After major content changes
- As a weekly maintenance task to verify configurations
PHP-FPM Calculation Process
Optimization Workflow
Step 1
Check Free RAM
(htop)
→
Step 2
Apply 90% Buffer
(Free RAM × 0.9)
→
Step 3
Check Pool Memory
(per site)
→
Step 4
Calculate pm.max_children
(divide & allocate)
Example Calculation Scenario
Server Configuration:
- Total RAM: 1.5 GB free
- Number of Sites: 2
- Site 1 Average Memory: 25 MB per child process
- Site 2 Average Memory: 30 MB per child process
Step-by-Step Calculation:
1. Apply Safety Buffer:
1.5 GB × 0.9 = 1.35 GB (allowing 10% buffer)
2. Convert to MB:
1.35 GB = 1,350 MB
3. Allocate Per Site:
1,350 MB ÷ 2 sites = 675 MB per site
4. Subtract Process Buffer (Site 1):
25 MB - 5 MB buffer = 20 MB per child process
5. Subtract Process Buffer (Site 2):
30 MB - 5 MB buffer = 25 MB per child process
6. Calculate pm.max_children (Site 1):
675 MB ÷ 20 MB = 33.75 → 33 (rounded down)
7. Calculate pm.max_children (Site 2):
675 MB ÷ 25 MB = 27 → 27
💡 Pro Tip: These values are starting points. For static, low-traffic sites, you can
reduce allocation and reallocate memory to high-traffic sites. Monitor and adjust weekly based on actual
usage patterns.
Server Memory Visualization
Example: 1 GB RAM Server Distribution
Used: 283 MB
Free: 673 MB
Available for PHP-FPM (90% buffer): 600 MB
RAM Scaling Impact
| Server RAM |
Free RAM (Est.) |
90% Buffer |
Per Site (2 sites) |
pm.max_children (55MB/process) |
| 1 GB |
673 MB |
606 MB |
303 MB |
5 |
| 2 GB |
1,700 MB |
1,530 MB |
765 MB |
13-14 |
| 4 GB |
3,766 MB |
3,389 MB |
1,694 MB |
30 |
✓ Key Insight: Server RAM is crucial for multi-site hosting. Doubling RAM more than
doubles your capacity to handle concurrent PHP processes efficiently.
Step-by-Step Implementation Guide
Step 1: Check Available Memory
Use htop to determine free server memory:
htop
Look for the memory line showing total and used RAM. Calculate: Total - Used = Free RAM
Example: 956 MB (total) - 283 MB (used) = 673 MB (free)
Apply 90% buffer: 673 MB × 0.9 = 606 MB → Round down to 600 MB
Press q to quit htop.
Step 2: Identify PHP Pools
List all configured PHP pools on your server:
grep -E '^\s*user\s*=' /etc/php/8.3/fpm/pool.d/*.conf | awk -F= '{print $2}'
| xargs | tr ' ' '\n' | sort -u
This command will display all pool users, for example:
- expert_wp
- nginx_help
- www-data (default pool)
Step 3: Measure Pool Memory Usage
Check memory consumption for each pool individually:
ps -C php-fpm --user pool_user -o rss= | awk '{ sum += $1; count++ } END { if
(count > 0) printf ("%d%s\n", sum/NR/1024,"M") }'
Replace pool_user with your actual pool username (e.g., expert_wp, nginx_help).
Step 4: Create Memory Monitoring Script
Create a bash script for automated pool memory checking:
cd ~
cd server_bash_scripts
nano pool_memory.sh
Script Contents:
#!/bin/bash
# Extract pool names
pools=$(grep -E '^\s*user\s*=' /etc/php/8.3/fpm/pool.d/*.conf | awk -F= '{print $2}' | xargs | tr ' '
'\n' | sort -u)
# Display memory used by each pool
for pool in $pools; do
memory_used=$(ps -C php-fpm --user "$pool" -o rss= | awk '{ sum += $1; count++ } END { if (count > 0)
printf ("%d%s\n", sum/NR/1024,"M") }')
echo -e "Pool: $pool\nMemory Used: $memory_used\n"
done
Make the script executable and run it:
chmod +x pool_memory.sh
sudo ./pool_memory.sh
Step 5: Switch Pool Mode to Dynamic (Temporary)
If a pool shows zero memory usage, temporarily switch from "on-demand" to "dynamic" mode to generate
measurable processes:
cd /etc/php/8.3/fpm/pool.d/
sudo nano expert_wp.conf
Find the line pm = ondemand and change it to:
pm = dynamic
Save and reload PHP-FPM:
sudo systemctl reload php8.3-fpm
Visit your website in a browser to generate traffic, then re-run the memory script.
Step 6: Verify Updated Memory Values
After generating traffic, run htop again to check updated memory usage:
htop
Recalculate free memory with the new values and apply the 90% buffer.
Step 7: Calculate pm.max_children Values
Using the calculations from earlier sections, determine appropriate values for each pool.
Example Calculation:
- Free Memory: 550 MB (after applying buffer)
- Number of Sites: 2
- Memory per Site: 550 MB ÷ 2 = 275 MB
- Average Memory per Process: 55 MB (after 5 MB buffer)
- pm.max_children: 275 MB ÷ 55 MB = 5
Step 8: Configure PHP Pool Settings
Edit the PHP pool configuration file:
cd /etc/php/8.3/fpm/pool.d/
sudo nano expert_wp.conf
Set the following directives:
; Process Manager Mode
pm = ondemand
; Maximum Child Processes
pm.max_children = 5
; Process Idle Timeout (seconds)
pm.process_idle_timeout = 10s
; Maximum Requests per Child
pm.max_requests = 500
Repeat for all site pool configuration files, then reload PHP-FPM:
sudo systemctl reload php8.3-fpm
💡 Important: Always use pm = ondemand for production. This mode creates
child processes only when needed, reducing idle memory consumption.
Monitoring and Troubleshooting
Monitor PHP-FPM Log File
Check for the critical "max_children reached" warning:
sudo grep "max" /var/log/php8.3-fpm.log
Warning Message Example:
WARNING: pool nginx_help server reached pm.max_children setting (5), consider raising it
⚠️ Action Required: If you see this warning, your
pm.max_children value is
too low. You must either:
- Increase server RAM and recalculate values for all sites
- Reduce pm.max_children for lower-traffic sites and reallocate to the affected site
Create OPcache File Monitoring Script
Monitor PHP file counts against OPcache configuration:
cd ~/server_bash_scripts
nano opcache_files.sh
Script Contents:
#!/bin/bash
echo ""
echo "Checking opcache.max_accelerated_files directive in each PHP pool file"
echo ""
POOL_DIR="/etc/php/8.3/fpm/pool.d/"
for file in "$POOL_DIR"*.conf; do
filename=$(basename "$file")
if grep -q "^[^#;]*opcache.max_accelerated_files" "$file"; then
value=$(grep "^[^#;]*opcache.max_accelerated_files" "$file" | awk -F'=' '{print $2}' | tr -d ' ')
echo "File: $filename - opcache.max_accelerated_files: $value"
else
echo "File: $filename - opcache.max_accelerated_files directive not found"
fi
done
echo ""
echo "Listing sites and PHP file counts - compare to pool file values"
echo ""
WWW_DIR="/var/www/"
for domain_dir in "$WWW_DIR"*/ ; do
if [ "$domain_dir" == "/var/www/html/" ]; then
continue
fi
domain_name=$(basename "$domain_dir")
public_html_dir="$domain_dir/public_html/"
if [ -d "$public_html_dir" ]; then
php_file_count=$(find "$public_html_dir" -type f -name "*.php" | wc -l)
echo "Domain: $domain_name - PHP files: $php_file_count"
else
echo "Domain: $domain_name - public_html directory not found"
fi
done
echo ""
Make executable and run:
chmod +x opcache_files.sh
sudo ./opcache_files.sh
Best Practices and Recommendations
Weekly Maintenance Checklist
☐ Run htop and verify free RAM availability
☐ Execute pool memory monitoring script
☐ Check PHP-FPM log file for "max_children" warnings
☐ Recalculate 90% buffer from current free memory
☐ Adjust pm.max_children values if needed
☐ Test configuration and reload PHP-FPM
☐ Monitor site performance after changes
Important Configuration Guidelines
| Setting |
Recommended Value |
Purpose |
pm |
ondemand |
Creates processes only when needed, reducing idle memory usage |
pm.max_children |
Calculated per site |
Maximum number of child processes; prevents memory exhaustion |
pm.process_idle_timeout |
10s |
Time before idle processes are terminated |
pm.max_requests |
500 |
Number of requests before child process respawns (prevents memory leaks) |
Key Considerations
- Caching Impact: Nginx caching significantly reduces PHP-FPM load. Cached content
doesn't require PHP processing, minimizing resource usage.
- Traffic Patterns: Low-traffic sites can use lower pm.max_children values, freeing
resources for high-traffic sites.
- Default Pool: Ignore the www-data pool when tuning; it's only used for phpMyAdmin
and doesn't serve site traffic.
- Safety First: Always round down calculations and maintain a 10% memory buffer to
prevent crashes.
- Documentation: Keep notes of your calculations and configurations for future
reference and troubleshooting.
💡 Pro Tip: If you receive "max_children reached" warnings, take screenshots of htop
and copy the log file warnings. Post these in the course Q&A for personalized guidance and optimization
recommendations.
PHP-FPM Process Manager Modes Explained
| Mode |
Behavior |
Use Case |
Memory Profile |
| ondemand |
Creates processes only when requests arrive |
Production servers with variable traffic |
Low idle memory, responsive to demand |
| dynamic |
Maintains a pool of spare processes |
High-traffic sites requiring instant response |
Higher idle memory, faster initial response |
| static |
Fixed number of processes always running |
Consistent high-volume traffic servers |
Highest memory usage, most predictable |
✓ Recommended: Use ondemand mode for most WordPress hosting scenarios. It
provides the best balance between resource efficiency and performance.
Common Issues and Solutions
Issue 1: Zero Memory Usage Reported
Symptom: Pool memory script returns 0 MB for a pool.
Cause: Pool is in "ondemand" mode with no active requests.
Solution: Temporarily switch to "dynamic" mode, generate traffic by browsing the site,
then measure memory usage. Switch back to "ondemand" after calculation.
Issue 2: Server Crashes or Becomes Unresponsive
Symptom: Server freezes or crashes under load.
Cause: pm.max_children values too high, causing memory exhaustion.
Solution: Lower pm.max_children values or upgrade server RAM. Always maintain 10% free
memory buffer.
Issue 3: Slow Page Load Times
Symptom: Pages load slowly despite adequate server resources.
Cause: pm.max_children value too low, causing request queuing.
Solution: Check PHP-FPM logs for "max_children reached" warnings. Increase value or
reallocate resources from other pools.
Issue 4: Memory Usage Increases After Updates
Symptom: Pool memory consumption rises after WordPress/plugin updates.
Cause: New code requires more resources per process.
Solution: Re-measure pool memory usage and recalculate pm.max_children values. This is
why weekly monitoring is crucial.
Summary and Key Takeaways
✓ Essential Points to Remember
- PHP-FPM optimization is NOT a "set and forget" configuration
- Always apply a 90% buffer to free RAM calculations
- Monitor and adjust pm.max_children values weekly
- Use "ondemand" mode for production environments
- Check PHP-FPM logs regularly for warnings
- Server RAM is crucial for multi-site hosting success
- Caching dramatically reduces PHP-FPM resource requirements
- Round down all calculations to maintain safety margins
Critical Formula Reference
pm.max_children = (Free RAM × 0.9 ÷ Number of Sites) ÷ Average Memory per Process
Remember to subtract 5MB buffer from average
process memory before calculation
📚 Additional Resources: For personalized optimization guidance, post your htop
screenshots and PHP-FPM log warnings in the course Q&A section. Include your specific server
configuration for tailored recommendations.
Professional Server Management
This guide is based on industry best practices for WordPress hosting optimization on NGINX servers with
PHP-FPM.
Last Updated: February 2026