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 their are so many variables that it makes 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.
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 all together 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.