Table of contents

 

Issue

We ran into a memory issue with one of our AWS Lightsail instances where the memory kept getting used up to 80%-90% and staying like that forever. The only moment it went down again was if we restarted Apache and PHP.

Since it was a 8GB instance and the application that ran in there had no way of using that amount of memory, I started to investigate the issue.

I quickly got to the PHP-FPM settings file where all the pm options were generated by Lightsail when the instance was deployed. Those values didn’t mean much to me, but compared to other servers they seemed pretty high.

The instance has a pm set to dynamic with the number of child processes set to high compared to the available memory and the memory used by the PHP-FPM process, which resulted in random crashes after a few months.

PHP-FPM settings

pm (process manager) has the following options:

  • static
  • dynamic
  • ondeman

Static

This will always keep the same number of child processes alive/available to handle requests.

The only setting that you’re going to configure is the pm.max_children. That will spawn the number of child processes you indicated and always keep them alive.

pm=static
pm.max_children=12

Dynamic

  • pm.max_children – the maximum number of child processes allowed.
  • pm.start_servers – the number of child processes when PHP-FPM starts.
  • pm.min_spare_servers – the minimum number of idle child processes that will be created. If current idle child processes lower than this value, more will be created.
  • pm.max_spare_servers – the maximum number of idle child processes allowed. If current idle child processes  higher than this value, some will be killed.
  • pm.max_requests – the number of requests a child process will execute before being killed and spawned again.
  • pm.process_idle_timeout – the idle time (seconds) after which a process child is killed.

The pm.max_requests and pm.process_idle_timeout exclude each other in most cases, so pick one of them.

If you go with pm.max_requests, use 500 or 1000 as values, not some ridiculous high values. You want the process to be terminated and spawned to prevent memory leaks for example.

pm=dynamic
pm.max_children=12
pm.start_servers=2
pm.min_spare_servers=1
pm.max_spare_servers=3
pm.max_requests=1000

ondemand

No process will be spawned by default, they will appear based on demand up to the maximum you set.

They will decrease again if there is no load and you might end-up with no process at all. This method might affect the user interaction if they are the ones accessing in the moment of no process existing (it depends on the complexity of the request).

pm=ondemand
pm.max_children=12
pm.max_requests=1000

Determine the PHP-FPM memory usage

In order to determine the correct number of child processes, you will need to estimate how much memory your PHP-FPM processes use.

Luckily there are some simple ways of doing that.

This will output your PHP-FPM processes and with some information, in column RSS you get the amount of memory used by that process

ps -ylC php-fpm --sort:rss

This one will output an average already calculated from the processes mentioned above

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

Let’s say the value is 100 MB to make it easy for us to do the math.

Calculate the correct number of child processes

Now that you know a process on average will use 100 MB of memory you can calculate the max number of child processes you can use.

If you run a 4 GB instance, depending on the OS and all the other programs that might be between 700 MB1.2 GB. That will leave you with 2.8 GB of memory for your application.

2800 MB / 100 MB = 28 child processes

That would be the maximum amount: pm.max_children.

Given that that is an average, I would go with a max of 25, just to have some spare memory left.

Based on that value, you can now configure your PHP-FPM settings.

static

pm=static
pm.max_children=25

dynamic

pm=dynamic
pm.max_children=25
pm.start_servers=8
pm.min_spare_servers=2
pm.max_spare_servers=4
pm.max_requests=1000

ondemand

pm=ondemand
pm.max_children=25
pm.max_requests=1000

What option should I choose?

If you’re running multiple sites/apps on a VPS where all the resources are shared I would suggest go with ondemand or dynamic. This way you don’t destroy the experience for the other apps running.

Make sure to play with the dynamic settings to help you achieve the correct result. For example pm.start_servers can make a big impact on the constant memory that is being used.

If you’re running a VPS dedicated to a single site/app than you can go with static, that will give you the best performance by having the maximum child processes always available and capable to work well even when you get a spike in traffic.

Resources