Article directory
The number of concurrent users a server can handle depends not on how many cores it has, but on how much memory each process consumes.
This statement may sound like a provocation, but it is the most real and painful experience in the operations and maintenance industry.
Why is memory the key bottleneck?
Many people see a VPS with an 8-core CPU and 24GB of memory and subconsciously think that they can easily run hundreds of PHP-FPM processes.
However, in reality, the RSS memory usage of a single PHP process is often as high as 200MB.
This is the result obtained through actual testing via command line:
ps --no-headers -o rss -p $(pgrep php-fpm) | awk '{sum+=$1} END {print sum/NR/1024 " MB"}'
In HestiaCP 's complex framework and multi-plugin environment, especially without OPcache optimization, 200MB is the norm.
This means that memory, not CPU, is the hard limit that determines maximum concurrency.

Recommended configuration file (php-fpm.conf)
pm = dynamic
pm.max_children = 80
pm.start_servers = 16
pm.min_spare_servers = 8
pm.max_spare_servers = 24
pm.max_requests = 500
pm.process_idle_timeout = 10s
request_terminate_timeout = 60s
Parameter calculation logic and setting basis
| Configuration parameters | Setting value | Core calculation and setup basis |
|---|---|---|
| pm | dynamic | Dynamic mode allows for the elastic addition or deletion of processes based on concurrency requirements, balancing response speed and memory utilization. |
| pm.max_children | 80 | 24GB total memory minus system kernel and MySQL/RedisAfter Nginx, approximately 16GB is left for PHP.16,384 MB / 200 MB ≈ 81.9Setting it to 80 can completely eliminate OOM (Out of Memory) during high concurrency. |
| pm.start_servers | 16 | Preheating at startup is set to twice the number of CPU cores (8 cores × 2 = 16) to ensure that the service can instantly handle basic concurrency after restarting. |
| pm.min_spare_servers | 8 | Set the CPU core count to 8 cores to ensure that new requests can be responded to at any time during low traffic periods. |
| pm.max_spare_servers | 24 | Set it to 3 times the number of CPU cores (8 cores × 3 = 24) to retain a moderate number of processes after the traffic recedes, in order to handle gentle fluctuations. |
| pm.max_requests | 500 | If a single process reaches a large base of 200MB, reducing the number of requests to 500 before destroying and rebuilding can clean up implicit memory leaks more quickly. |
| pm.process_idle_timeout | 10s | Exceeding min_spare_servers Idle processes are automatically released and returned to system memory after 10 seconds of no requests. |
Essential Supporting Settings and Optimization Suggestions
1. Timeout anti-hangover mechanism
request_terminate_timeout = 60s is the key.
It can forcibly terminate processes that are stuck due to database deadlock or third-party API blocking.
Meanwhile, Nginx's fastcgi_read_timeout It must be kept at least 60 seconds, otherwise the client will receive it prematurely. 504 Gateway Timeout.
2. Strategies to overcome memory bottlenecks
A maximum of 80 concurrent processes means that under extreme concurrency conditions, the system can handle a maximum of 80 dynamic HTTP requests simultaneously.
If you want to further improve capacity, the focus should be on reducing the memory usage per process.
- Enable OPcache:in
php.iniMedium configurationopcache.enable=1Andopcache.memory_consumption=256Bytecode caching can reduce the memory usage of a single process from 200MB to 60~100MB. - Reasonable control of memory_limit:Will
php.inimiddlememory_limitLimited to128MOr256MTo prevent individual abnormal scriptsunlimitedIt consumes a lot of memory.
Once the memory usage of a single process drops to 100MB...pm.max_children It can be safely upgraded to 150 In addition, the concurrency capability has almost doubled.
Authoritative viewpoints cited
According to the recommendations in the official Nginx documentation :
"FastCGI applications should always be monitored with timeout directives to prevent resource exhaustion."
(Source: Nginx Docs)
The official PHP manual clearly states:
"pm.max_children defines the maximum number of child processes to be created. This is the most important directive."
(Source: PHP-FPM Documentation)
These authoritative views are perfectly aligned with our practices, proving that optimization logic is not only based on experience, but also on standardized best practices.
Conclusion: My Viewpoints and Key Quotes
In high-concurrency scenarios, the CPU is the engine, the memory is the fuel tank, and PHP-FPM is the fleet dispatcher.
No matter how powerful the engine is, if the fuel tank isn't big enough, the convoy won't go very far.
True experts don't blindly max out the parameters, but rather precisely calculate the memory usage of each process to avoid both waste and overflow.
The essence of optimization is to find the optimal balance with limited resources.
This is not just a technology, but also a philosophy.
Therefore, stop believing that "the number of cores determines everything." What truly determines the concurrency limit is your control over memory.
Take action and optimize your VPS to its fullest potential, making the most of every drop of memory.
Hopefully, the article "8-core 24GB VPS running out of memory? Extreme tuning of PHP-FPM process pool under HestiaCP" shared on Chen Weiliang's blog ( https://www.chenweiliang.com/ ) will be helpful to you.
Feel free to share this article's link: https://www.chenweiliang.com/cwl-34509.html
