System administrators and web developers frequently encounter sudden CPU spikes where php-fpm processes consume 100% or more of available CPU resources. This often occurs on self-hosted stacks like Nginx and MediaWiki after modifying configuration files, invalidating file caches, or getting hit by automated web crawlers. When this happens, system monitoring tools like top or htop show multiple worker processes maxing out system cores continuously.
Quick fix
- Enable the PHP-FPM slow log to identify the exact script, function, or database query consuming CPU cores.
- Reset and rebuild MediaWiki cache files rather than letting live bot traffic dynamically regenerate all pages simultaneously.
- Tune PHP-FPM process manager directives (
pm.max_children,pm.max_requests) to prevent worker exhaustion. - Ensure Zend OPcache is enabled and allocated enough memory to keep compiled PHP bytecode in RAM.
- Implement rate limiting on Nginx for aggressive web spiders and search engine bots.
Why this happens
- Cache Stampede (Thundering Herd): Touch-updating or editing files like MediaWiki's
LocalSettings.phpinvalidates page caches. When bots immediately request hundreds of uncached pages, PHP-FPM tries to parse, compile, and render every page concurrently, spiking CPU load to max limits. - Disabled or Exhausted OPcache: Without bytecode caching—or if OPcache memory is full—PHP re-parses and re-compiles script files on every single HTTP request.
- Unconstrained PHP-FPM Pool Settings: Settings like
pm = dynamicwith excessivepm.max_childrenallow PHP-FPM to spawn more processes than physical CPU cores and RAM can sustain simultaneously. - Aggressive Crawler Traffic: Search engine bots (Googlebot, Bingbot) requesting uncached wiki pages concurrently can easily overpower small Virtual Private Servers (VPS).
How to fix it
1. Identify the CPU-hungry script with PHP-FPM Slow Log
To find out exactly what PHP-FPM is doing when CPU usage surges, turn on the built-in request tracer. Before editing system files, create a backup of your configuration.
sudo cp /etc/php/8.3/fpm/pool.d/www.conf /etc/php/8.3/fpm/pool.d/www.conf.bak
Open your pool configuration file (adjust the version number 8.3 to match your installed PHP version):
sudo nano /etc/php/8.3/fpm/pool.d/www.conf
Find and update the following directives to record execution details for any request taking longer than 2 seconds:
request_slowlog_timeout = 2s
slowlog = /var/log/php-fpm/www-slow.log
Create the log directory if it does not exist and restart PHP-FPM:
sudo mkdir -p /var/log/php-fpm
sudo systemctl restart php8.3-fpm
Inspect the output in real-time using tail to pinpoint the stalling function calls or database queries:
sudo tail -f /var/log/php-fpm/www-slow.log
2. Handle MediaWiki Cache Invalidation and Rebuilding
If touching LocalSettings.php invalidated MediaWiki's file cache, letting traffic dynamically rebuild the cache causes severe performance degradation. Instead, pre-generate the cache using MediaWiki's CLI maintenance scripts.
First, clear any corrupted cache entries and warm up the cache manually from the command line as your web user or root:
cd /var/www/html/mediawiki
sudo -u www-data php maintenance/run.php clearCache
sudo -u www-data php maintenance/run.php rebuildFileCache
Note: On older MediaWiki versions (1.39 and below), run sudo -u www-data php maintenance/rebuildFileCache.php directly inside the maintenance directory.
3. Configure Zend OPcache Correctly
Verify that OPcache is active and has sufficient memory allocation. Edit your PHP CLI and FPM php.ini files (typically located in /etc/php/8.3/fpm/php.ini):
opcache.enable=1
opcache.memory_consumption=128
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=10000
opcache.validate_timestamps=1
opcache.revalidate_freq=2
Restart PHP-FPM after saving changes:
sudo systemctl restart php8.3-fpm
4. Tune PHP-FPM Process Manager (pm) Directives
Excessive worker processes overwhelm CPU thread queues. Open /etc/php/8.3/fpm/pool.d/www.conf and adjust process limits according to available RAM and core count:
pm = dynamic
pm.max_children = 10
pm.start_servers = 3
pm.min_spare_servers = 2
pm.max_spare_servers = 4
pm.max_requests = 500
Setting pm.max_requests ensures that workers periodically recycle, clearing memory leaks caused by complex PHP extensions.
5. Apply Rate Limiting in Nginx for Bots
To prevent web crawlers from overpowering your backend during cache rebuilding, add rate-limiting rules. Open the main Nginx configuration file /etc/nginx/nginx.conf (rate limits must be defined inside the top-level http block):
http {
# Define rate limiting zone inside the http block
limit_req_zone $binary_remote_addr zone=bot_limit:10m rate=5r/s;
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
Next, apply this rate limit inside your specific website's server block file (e.g., /etc/nginx/sites-available/example.com):
server {
server_name example.com www.example.com;
location / {
limit_req zone=bot_limit burst=10 nodelay;
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
}
Test and reload Nginx configuration:
sudo nginx -t
sudo systemctl reload nginx
Check that it worked
To confirm that CPU usage has normalized, check running processes using htop or system status tools:
top -b -n 1 | grep php-fpm
Average CPU load should drop back down to safe thresholds. Additionally, query active PHP status endpoints or inspect your slow log to ensure new execution traces are no longer appending stalled requests:
sudo tail -n 20 /var/log/php-fpm/www-slow.log
If the output stops generating long stack traces, PHP-FPM is processing requests normally without bottlenecks.
Prevent it next time
- Always warm up application file caches using CLI scripts after updating system configuration files.
- Maintain clear
robots.txtcrawl-delay directives or restrict aggressive search engine bots during system maintenance windows. - Monitor long-running tasks by integrating automated alerts or review our guide on how to kill slow MySQL queries automatically to keep database delays from queuing PHP execution threads.
- Implement an edge CDN like Cloudflare to absorb static request traffic before it reaches your PHP backend.
FAQ
Why did modifying a configuration file spike my server CPU?
Editing settings files often changes modification timestamps, which triggers automatic cache invalidation in CMS applications and PHP bytecode caches. Active traffic then forces your server to recompile scripts and rebuild page caches concurrently.
What is the difference between static and dynamic PHP-FPM process management?
The static mode maintains a fixed number of worker processes running regardless of load, offering faster execution at the cost of constant memory usage. The dynamic mode scales workers up and down based on traffic load, making it better for environments with constrained RAM.
How do I know if my PHP-FPM pool max_children limit is too low?
If pm.max_children is set too low, your web server log will output warnings such as [WARNING] [pool www] server reached pm.max_children setting, consider raising it, and end users will experience 502 Bad Gateway or 504 Gateway Timeout errors.
How Dhruva Cloud handles PHP execution
Managed hosting environments eliminate the complexity of manual PHP process tuning. On Dhruva Cloud PHP hosting, your site is automatically deployed behind a high-performance Cloudflare CDN with built-in edge caching, DDoS protection, and secure PHP execution stacks, so server resources stay balanced even during unexpected traffic spikes.