Why I switched to nginx

When I originally setup my VPS I didn’t think twice about using Apache for my web serving duties. Setting up the LAMP environment was very easy and everything that I needed to host worked perfectly with this setup. However, when I decided to create my blog using Rails, I knew I would have to modify my setup. Originally I had planned to use mod_proxy_balancer and mongrel, but then Phusion’s Passenger came out and that was easy enough to get running. However, I was itching to try something new, improve the speed of my sites and lower the amount of resources that were being used on my Slice. Enter Nginx.

Here’s why I decided to give Nginx a shot:

  • Nginx is lightweight and has a small memory footprint
  • Nginx is super fast
  • Nginx can be easily configured to send rails requests to mongrel, thin and others
  • Nginx has easy to read configuration logs and rewriting rules
  • I get to learn something new!

Note: All ApacheBench test results displayed are the from the 4th consecutive test. This minimizes the negative effects of the slow startup of ruby processes with Phusion Passenger where the initial requests take a considerably long time.

Apache vs. Nginx Memory Usage

For this test I ran ApacheBench against the index page of a well trafficked PunBB forum that I host on my slice. I fired up top and found the following:

Apache

Mem: 262316k total, 207140k used, 55176k free, 4280k buffers

Nginx

Mem: 262316k total, 146956k used, 115360k free, 3844k buffers

That’s about 40MB less with Nginx. That makes a big difference when you’re only starting out with 256MB.

Apache vs. Nginx AB results

Apache

This is ApacheBench, Version 2.0.41-dev <$Revision: 1.141 $> apache-2.0
		Copyright (c) 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
		Copyright (c) 1998-2002 The Apache Software Foundation, http://www.apache.org/
 
		Benchmarking www.example.com (be patient).....done
 
 
		Server Software:        Apache
		Server Hostname:        www.example.com
		Server Port:            80
 
		Document Path:          /
		Document Length:        8857 bytes
 
		Concurrency Level:      10
		Time taken for tests:   1.732965 seconds
		Complete requests:      100
		Failed requests:        0
		Write errors:           0
		Total transferred:      928532 bytes
		HTML transferred:       897838 bytes
		Requests per second:    57.70 [#/sec] (mean)
		Time per request:       173.296 [ms] (mean)
		Time per request:       17.330 [ms] (mean, across all concurrent requests)
		Transfer rate:          522.80 [Kbytes/sec] received
 
		Connection Times (ms)
		              min  mean[+/-sd] median   max
		Connect:       22   26   4.3     26      40
		Processing:    70  136  41.5    133     268
		Waiting:       46  105  40.9    100     239
		Total:         93  163  42.4    160     294
 
		Percentage of the requests served within a certain time (ms)
		  50%    160
		  66%    174
		  75%    186
		  80%    197
		  90%    213
		  95%    250
		  98%    274
		  99%    294
		 100%    294 (longest request)

Nginx

	This is ApacheBench, Version 2.0.41-dev <$Revision: 1.141 $> apache-2.0
	Copyright (c) 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
	Copyright (c) 1998-2002 The Apache Software Foundation, http://www.apache.org/
 
	Benchmarking www.example.com (be patient).....done
 
 
	Server Software:        Nginx/0.6.32
	Server Hostname:        www.example.com
	Server Port:            8080
 
	Document Path:          /
	Document Length:        8858 bytes
 
	Concurrency Level:      10
	Time taken for tests:   1.107068 seconds
	Complete requests:      100
	Failed requests:        0
	Write errors:           0
	Total transferred:      929232 bytes
	HTML transferred:       897920 bytes
	Requests per second:    90.33 [#/sec] (mean)
	Time per request:       110.707 [ms] (mean)
	Time per request:       11.071 [ms] (mean, across all concurrent requests)
	Transfer rate:          819.28 [Kbytes/sec] received
 
	Connection Times (ms)
	              min  mean[+/-sd] median   max
	Connect:       22   25   4.3     25      42
	Processing:    62   80  29.4     75     345
	Waiting:       37   49  10.6     47      93
	Total:         85  106  29.5    100     368
 
	Percentage of the requests served within a certain time (ms)
	  50%    100
	  66%    105
	  75%    109
	  80%    112
	  90%    119
	  95%    146
	  98%    147
	  99%    368
	 100%    368 (longest request)

There are a few things that stand out to me from these tests:

  1. Requests Per Second: 90 vs 58, Nginx wins
  2. Mean Processing Time: 80 vs 136, Nginx wins
  3. Percentage of Requess served within 100ms: 50% vs. ??, Nginx Wins

Nginx and Thin vs. Apache and Phusion Passenger

So the PHP/MySQL stuff was all fine and good and that’s something that Apache and Nginx do very well, so no real surprises there. What about handling requests to a simple rails application like this blog?

For this test I again ran apache bench against the non-cached version of my root url. On this page there are some partials being rendered, active record database calls, images loading from Amazon’s S3 and some static files being served.

Apache 2.2 + Phusion Passenger 2.0.3

	This is ApacheBench, Version 2.0.41-dev <$Revision: 1.141 $> apache-2.0
	Copyright (c) 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
	Copyright (c) 1998-2002 The Apache Software Foundation, http://www.apache.org/
 
	Benchmarking www.lengelzigich.com (be patient).....done
 
 
	Server Software:        Apache
	Server Hostname:        www.lengelzigich.com
	Server Port:            80
 
	Document Path:          /
	Document Length:        14894 bytes
 
	Concurrency Level:      10
	Time taken for tests:   26.118296 seconds
	Complete requests:      100
	Failed requests:        0
	Write errors:           0
	Total transferred:      1544644 bytes
	HTML transferred:       1493235 bytes
	Requests per second:    3.83 [#/sec] (mean)
	Time per request:       2611.830 [ms] (mean)
	Time per request:       261.183 [ms] (mean, across all concurrent requests)
	Transfer rate:          57.74 [Kbytes/sec] received
 
	Connection Times (ms)
	              min  mean[+/-sd] median   max
	Connect:       22   24   1.5     25      27
	Processing:   769 2477 389.8   2524    3076
	Waiting:      744 2448 387.8   2498    3048
	Total:        792 2501 390.1   2547    3102
 
	Percentage of the requests served within a certain time (ms)
	  50%   2547
	  66%   2637
	  75%   2793
	  80%   2819
	  90%   2906
	  95%   2963
	  98%   3079
	  99%   3102
	 100%   3102 (longest request)

Nginx + Thin

I decided to use thin instead of mongrel because, well, I guess I just wanted to try something new. I heard that thin had recently hit version 1.0 and figured it was worth checking out.

This is ApacheBench, Version 2.0.41-dev <$Revision: 1.141 $> apache-2.0
Copyright (c) 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Copyright (c) 1998-2002 The Apache Software Foundation, http://www.apache.org/
 
Benchmarking www.lengelzigich.com (be patient).....done
 
 
Server Software:        Nginx/0.6.32
Server Hostname:        www.lengelzigich.com
Server Port:            80
 
Document Path:          /
Document Length:        14894 bytes
 
Concurrency Level:      10
Time taken for tests:   17.334487 seconds
Complete requests:      100
Failed requests:        0
Write errors:           0
Total transferred:      1534900 bytes
HTML transferred:       1489400 bytes
Requests per second:    5.77 [#/sec] (mean)
Time per request:       1733.449 [ms] (mean)
Time per request:       173.345 [ms] (mean, across all concurrent requests)
Transfer rate:          86.42 [Kbytes/sec] received
 
Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:       22   24   1.4     25      26
Processing:   526 1663 489.6   1544    2995
Waiting:      477 1613 491.6   1496    2949
Total:        549 1687 489.9   1567    3020
 
Percentage of the requests served within a certain time (ms)
  50%   1567
  66%   1882
  75%   1947
  80%   2034
  90%   2394
  95%   2648
  98%   3020
  99%   3020
 100%   3020 (longest request)

The results here are a little more impressive.

  1. Requests Per Second: 6 vs. 4, not great, but Nginx wins
  2. Mean Processing time: 1660 vs. 2448, Nginx wins
  3. Percentage of Requess served within 1560ms: 50% vs. ??, Nginx Wins

I don’t have the top memory output for these two tests handy, but with Apache free memory was hovering around 3MB. Nginx had free memory hovering around 12MB.

The key difference between Nginx and Apache is that with Apache there is a noticeable lag while Ruby processes are spun up by Passenger on the initial request, however, as I mentioned above, this is not reflected in these ApacheBench tests.

Plus, I Learned A Lot!

I think the best part of all of this was the overall learning experience. I broke out of my Apache shell and learned about a new web server, here are the highlights:

  • Learned how to install and configure Nginx, php-cgi, spawn-fcgi and thin on Ubuntu
  • Wrote new Capistrano deployment recipes to handle deploying to my new Nginx + thin setup
  • Created some new shell scripts to mimic common tasks from apache2ctl and a2ensite/a2dissite
  • Recreated my existing Apache virtual host setup with Nginx

If you’re currently running LAMP or using Apache and “mod rails”, give Nginx a shot. It’s easy to setup, doesn’t use too many resources, and best of all, it’s fast.

Speak Your Mind

*