Diagnosing and Resolving Nginx + PHP-FPM Memory Leaks and Performance Degradation in Magento Environments


2 views

When working with Magento on a Nginx + PHP-FPM stack, performance degradation over time is a common yet frustrating issue. After server reboot, everything runs smoothly, but within 24 hours, response times balloon to 2+ minutes per page. SSH sessions become sluggish, and CPU spikes occur despite low apparent utilization.

Your free -m output shows:


             total       used       free     shared    buffers     cached
Mem:          5963       5217        745          0        192        314
-/+ buffers/cache:       4711       1252
Swap:         4047          4       4042

This indicates significant memory usage (4.7GB actual used), but crucially shows minimal swap usage. The Linux memory management system is working as expected - cached memory isn't the issue here.

Looking at your top output, multiple php-fpm processes are consuming 50-120MB RSS each. For Magento, this isn't unusual, but the accumulation suggests either:

  • Improper PHP-FPM pool configuration
  • Memory leaks in PHP extensions
  • Unbounded session storage

Try these adjustments in your /etc/php-fpm.d/www.conf:


pm = dynamic
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 10
pm.max_requests = 500

For Magento specifically, add these PHP settings:


php_admin_value[memory_limit] = 512M
php_admin_value[realpath_cache_size] = 256K
php_admin_value[opcache.memory_consumption] = 256

Install and run this monitoring script to track PHP-FPM memory usage over time:


#!/bin/bash
while true; do
  date >> /var/log/phpfpm_mem.log
  ps -ylC php-fpm --sort:rss >> /var/log/phpfpm_mem.log
  sleep 300
done

Complement your PHP-FPM tuning with these Nginx settings in your Magento server block:


fastcgi_buffers 16 16k;
fastcgi_buffer_size 32k;
fastcgi_connect_timeout 300;
fastcgi_send_timeout 300;
fastcgi_read_timeout 300;

For temporary relief while investigating, set up a cron job to gracefully restart PHP-FPM during low-traffic periods:


0 3 * * * /bin/kill -USR2 $(cat /var/run/php-fpm/php-fpm.pid)

When working with Nginx + PHP-FPM configurations (especially for Magento), memory issues can creep up in unexpected ways. Based on your diagnostics, we're dealing with a classic "gradual performance degradation" scenario rather than immediate failure.

Your memory output shows:

-/+ buffers/cache:       4711       1252

This reveals the true available memory after accounting for disk caching. The 1.2GB free suggests your system isn't actually out of memory. The key indicators are:

  • Low swap usage (good)
  • Moderate cache usage (normal)
  • No obvious memory hog in top

Your top output shows multiple PHP-FPM processes consuming 50-120MB each. For a Magento setup, this isn't unreasonable, but the cumulative effect matters. Consider these tuning parameters in your www.conf:

pm = dynamic
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 10
pm.max_requests = 500

The max_requests parameter is particularly important - it gracefully restarts processes after serving X requests, preventing memory leaks.

Add these to your Magento's local.xml:

<cache>
    <backend>memcached</backend>
    <slow_backend>database</slow_backend>
</cache>

And configure PHP-FPM with these additional parameters:

php_admin_value[memory_limit] = 256M
php_admin_value[realpath_cache_size] = 256K
php_admin_value[opcache.enable] = 1

Add these to your nginx config for PHP handling:

fastcgi_buffers 16 16k;
fastcgi_buffer_size 32k;
fastcgi_busy_buffers_size 256k;
fastcgi_temp_file_write_size 256k;

Instead of just watching free -m, use these more revealing commands:

# Watch PHP-FPM memory usage per process
watch -n 5 'ps -ylC php-fpm --sort:rss | awk \'{sum+=$8; ++n} END {print "Tot="sum"("n") Avg="sum/n/1024"MB"}\''

# Real-time PHP-FPM status
watch -n 5 'curl -s http://localhost/php-fpm-status | grep -E "accepted conn|pool|idle processes"'

Magento's caching can actually cause memory issues when improperly configured. Verify your cache storage method:

# Check current cache configuration
n98-magerun.phar sys:info | grep -i cache

For emergency situations, create this watchdog script (/usr/local/bin/phpfpm_watchdog.sh):

#!/bin/bash
THRESHOLD=80
LOAD=$(cat /proc/loadavg | cut -d' ' -f1 | awk '{print int($1)}')
if [ "$LOAD" -gt "$THRESHOLD" ]; then
    systemctl restart php-fpm
    echo "$(date) - Restarted PHP-FPM due to load $LOAD" >> /var/log/phpfpm_watchdog.log
fi

Add to cron:

* * * * * /usr/local/bin/phpfpm_watchdog.sh