mod_php vs php-fpm Performance Benchmark [Surprising Results]

mod_php vs php-fpm Performance Benchmark [Surprising Results] banner

Have you ever wondered if Apache mod-php is faster than php-fpm with Nginx? I surely did.

Each time this question arises, I do a quick research, and I cannot seem to find a definitive answer. It seems that the recommended way to run PHP websites nowadays is to use php-fpm with Nginx, but is that the best method? Some articles state that php-fpm is 300% faster than mod-php, other articles state that mod-php is faster, and so on.

This is why I've decided to conduct my own tests and performance benchmark and settle this debate once and for all.

The result surprised me.

The test environment

  • Machine: Hetzner VPS with 2 dedicated AMD CPUs, 8GB RAM, 80GB SSD
  • Benchmark machine: separate VPS (in the same region)
  • Benchmark tool: bombardier (golang)
  • OS: Ubuntu 22.04
  • PHP version: PHP 8.2.9
  • mod-php web server: Apache/2.4.52 with mod-php 8.2.9
  • php-fpm web server: nginx/1.18.0 with php-fpm 8.2.9
  • php.ini custom config: 
    // php.ini
    // default config
    opcache.memory_consumption=256
    opcache.max_accelerated_files=20000
    opcache.validate_timestamps=0
    
    realpath_cache_size=4096K
    realpath_cache_ttl=600

    This is the suggested PHP configuration for maximum performance for Symfony production environments.
    Both servers use the same php.ini file!

Apache mpm prefork module configuration

# /etc/apache2/mods-available/mpm_prefork.conf
StartServers  5
MinSpareServers  50
MaxSpareServers  100
MaxRequestWorkers 150
MaxConnectionsPerChild  0

I've deliberately chosen a high value for MinSpareServers because more spare servers mean fewer cold starts and better performance in general.

Nginx process manager configuration

; /etc/php/8.2/fpm/pool.d/www.conf
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
pm.max_requests = 500

The test app

I chose Symfony PHP framework for the test app, and I've created a simple app with one page that renders some text. No database queries because they can distort the results.

Benchmark setup

The exact command to run the tests is

bombardier -c 200 -d 1m -l http://IP_HERE/

which translates to

  • Concurrent requests: 200
  • Duration: 1 minute

The results

Let's see how both methods compare in each category.

Average requests per second

In the average requests per second, the race is neck to neck with Nginx php-fpm having a slight advantage.

average requests per second bar chart

Average latency

In the average latency, Nginx with php-fpm again has a slight advantage over Apache mod php.

average latency bar chart

Max latency

In the max latency category, things get a little bit more interesting. 

It seems that Apache mod-php has a maximum latency of 10 seconds. I'll repeat - 10 seconds where Nginx php-fpm has a max latency of only 320.16ms.

Ten seconds of latency for a simple page is terrible and concerning.

Nginx php-fpm here is a clear winner by a lot.

max latency bar chart

Timeouts

Apache mod-php had 205 timeouts, whereas Nginx php-fpm had ZERO.

Again, Nginx php-fpm is the clear winner here.

Also, having that many timeouts is a bit concerning. That tells me the Apache limit was reached.

timeouts bar chart

Nginx php-fpm latency distribution

Nginx php-fpm shows stable latency distribution, which is a good thing! This means the performance is solid without fluctuations.

nginx php fpm latency distribution row chart

Apache mod-php latency distribution

I can't tell the same about Apache mod-php.

The latency distribution has a lot of fractionations. Again, this shows the Apache limit was reached.

apache mod php latency distribution row chart

Raw results

root@ubuntu-test-bench:~# bombardier -c 200 -d 1m -l http://phpfpm/
Bombarding http://phpfpm:80/ for 1m0s using 200 connection(s)
[===========================================] 1m0s
Done!
Statistics        Avg      Stdev        Max
  Reqs/sec       781.55      57.29     993.02
  Latency      255.43ms     9.08ms   320.16ms
  Latency Distribution
     50%   255.28ms
     75%   257.38ms
     90%   259.49ms
     95%   261.25ms
     99%   269.54ms
  HTTP codes:
    1xx - 0, 2xx - 47070, 3xx - 0, 4xx - 0, 5xx - 0
    others - 0
  Throughput:     0.90MB/s



root@ubuntu-test-bench:~# bombardier -c 200 -d 1m -l http://modphp/
Bombarding http://modphp:80/ for 1m0s using 200 connection(s)
[===========================================] 1m0s
Done!
Statistics        Avg      Stdev        Max
  Reqs/sec       690.88     130.38    3421.26
  Latency      289.55ms      0.86s     10.03s
  Latency Distribution
     50%   200.18ms
     75%   270.75ms
     90%   358.24ms
     95%   401.93ms
     99%      1.83s
  HTTP codes:
    1xx - 0, 2xx - 41425, 3xx - 0, 4xx - 0, 5xx - 0
    others - 205
  Errors:
       timeout - 205
  Throughput:   790.21KB/s

Conclusion

The performance tests and benchmarks have provided valuable insights into the comparison between Nginx with PHP-FPM and Apache with mod_php. The results point toward Nginx PHP-FPM as the clear winner in terms of performance and stability.

Nginx PHP-FPM stands out with better metrics such as higher average requests per second and lower average latency. It also excels in avoiding prolonged delays, as evidenced by its lead in maximum latency over Apache mod_php. 

Additionally, Nginx PHP-FPM is more reliable under pressure, as it doesn't experience timeouts like Apache mod_php, which had 205 timeouts. Nginx PHP-FPM's stable latency distribution further demonstrates its consistent performance, while Apache mod_php appears to struggle.

In summary, developers looking for both performance and reliability are encouraged to choose Nginx with PHP-FPM. These findings provide valuable guidance for optimizing web server configurations to enhance user experiences and improve application performance.

Additional resources