Have you ever spent hours tuning PHP-FPM, stress tested it until everything runs smoothly? You deploy your changes into production and within a few hours or days, it suddenly crashes? requiring a manual restart, whilst in the meantime taking your entire website offline? read on.
PHP-FPM is very tricky to tune correctly and there are so many variables that make it almost impossible to provide you with the ultimate settings. However, I can certainly guide you in the right direction to tune it for ultimate performance and reliability.
Common issues for PHP-FPM crashing after tuning
- You tuned and stress tested PHP-FPM on cached pages but forgot to see how it performs on non-cached pages
- You aren’t simulating enough connections, resulting in PHP-FPM running out of PM.Children and crashing
- You tuned PHP-FPM to use far more RAM than you actually have. This is a very common issue when you tune on a cached page, but not tested it on non-cached pages. Remember, a non-cached page needs to execute a lot of PHP, requiring far more RAM than serving a cached HTML page.
- Start by checking the current configuration settings by running the command
php-fpm -i. This will give you an overview of the current settings, including the version of PHP, the number of processes, and the current memory limits.
- Increase the number of processes by editing the
pm.max_childrensetting in the PHP-FPM configuration file (usually located at /etc/php-fpm.d/www.conf). This setting controls the maximum number of child processes that PHP-FPM can spawn. A good starting point is to set this to the number of CPU cores on your server.
- Adjust the amount of memory allocated to each process by editing the
pm.max_requestssetting. This setting controls the number of requests each child process can handle before being terminated and replaced with a new one. A higher value can help reduce memory usage and improve performance.
- Configure the PHP-FPM pool to use ondemand process management by editing the
pmsetting in the PHP-FPM configuration file. This means that PHP-FPM will only spawn new child processes when needed, rather than keeping a fixed number of processes running at all times.
- Finally, it’s a good practice to monitor the performance of PHP-FPM by using tools such as
htopto check the number of active processes, CPU usage, and memory usage.
Please note that these are general instructions and may vary depending on the specific version of PHP and the server configuration. Also, this is a starting point, and you may have to fine-tune the settings based on your specific use case and the traffic on your website.
Let’s give it a go
Now let’s begin, the settings you would need to change will live in the following file:
You would need to scroll down and find the following lines. Note: they are not altogether as shown below.
pm = dynamic
pm.max_children = 4
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
pm.max_requests = 200
These are the key settings which will heavily depend on how reliable PHP-FPM will be and much traffic it can handle.
Before we begin tuning for PHP-FPM you will need to use a server stress testing tool. I personally use an SEO tool called ScreamingFrog where I can set the crawl rate very high which will simulate traffic on random pages. However this is a paid tool. If you do not have access to this, you can use something like the Apache Stress test tool which can be installed using the following command
sudo apt-get install apache2-utils
In order to execute a stress test you would use the following command line
ab -c 100 -t 60 -r http://example.com/
-c 100 means 100 requests per second and -t 60 means for 60 seconds. It is strongly advised that you run this command from a different server then the one you are testing.
Here you will be able to see how many requests your server can handle, how long it took to complete the requests and if PHP-FPM crashes along with how much memory it’s using under load using ‘htop’.
This is where you would tweak the settings until you find the optimum performance. The key element here will be ‘pm.max_children’. Set this too high and your server will crash, set this too low and again your server will crash. This is heavily dependant on how much RAM your server has, how many CPU’s and what application is it running i.e. WordPress, Magento etc.
High numbers usually perform best for multi-core CPU servers serving cached pages. However, will likely crash PHP-FPM if you get a surge of request on non-cached pages. This is what makes this tricky to get right.
The second most important is ‘pm.max_requests’, after days of testing I always found setting this to 0 (unlimited) provides the best results when you are dealing with high concurrency.
I appreciate some of you may want actual figures, here’s one I did on an 8 core server with 16GB ram, running WordPress and full page caching in Redis.
pm = dynamic
pm.max_children = 12
pm.start_servers = 6
pm.min_spare_servers = 4
pm.max_spare_servers = 8
pm.max_requests = 0
The configuration above allowed me to test up to 1,400 requests per second before maxing out the servers bandwidth at 2GB/s.
Tip: when stress testing high concurrency, make sure you do not have other bottlenecks such as your server running out of TCP/IP connections, or Nginx keeping connections open for too long! this is a common issue with high concurrency where you can spend hours trying to get your settings right, not realising that PHP-FPM is not your bottleneck.