nginx is one hell of a webserver. It’s fast, efficient, small and reliable. According to Netcraft’s webserver survey about 3 percent of world’s webserver are using nginx to deliver content and the amount of nginx-powered servers is growing strong.
Since I was bothered by administrating another Apache (I do this all day long) and because I was tired of letting the Apache balance on several Mongrels which is not just slow, but also quite inefficient for this low-traffic blog, I just migrated my whole environment to be served by nginx. And that wasn’t much of a problem.
Here’s how I proceeded on a Debian Lenny server, enabling nginx to serve content via PHP and Ruby through Phusion’s Passenger, also known as mod_rails, at once.
Requirements
# build-essential isn't installed by default, just in case you haven't done this yet aptitude install bzip2 build-essential # Required ruby packages aptitude install ruby ruby-dev ruby1.8 rubygems libopenssl-ruby # FastCGI packages to make nginx work with PHP aptitude install libfcgi libfcgi-perl libfcgi # The whole php5 package (you might not need all of it) aptitude install php5 php5-cgi php5-common php5-dev php5-mysql php5-sqlite php5-tidy php5-xmlrpc php5-xsl php5-cgi php5-mcrypt php5-curl php5-gd php5-memcache php5-mhash php5-pspell php5-snmp php5-sqlite libmagick9-dev php5-cli
Get PHP going
To enable nginx to serve PHP-written content we have to retrieve something from the lighttpd project what is called spawnfcgi. This is necessary because nginx isn’t capable to handle PHP by default. We therefore have to get a copy of the lighttpd source, unpack, configure and make it. We then only copy the spawnfcgi binary to a local directory.
user@host ~ $ wget http://www.lighttpd.net/download/lighttpd-1.4.18.tar.bz2 user@host ~ $ tar xvjf lighttpd-1.4.18.tar.bz2 user@host ~ $ cd lighttpd-1.4.18/ user@host ~/lighttpd-1.4.18 $ ./configure user@host ~/lighttpd-1.4.18 $ make user@host ~/lighttpd-1.4.18 $ sudo cp src/spawn-fcgi /usr/bin/spawn-fcgi
After that we should give it a try to see whether it’s working:
user@host ~ $ sudo /usr/bin/spawn-fcgi -f /usr/bin/php-cgi -a 127.0.0.1 -p 49232 -P /var/run/fastcgi-php.pid user@host ~ $ ps aux|grep cgi root 31145 2.3 2.1 191944 11212 ? Ss 23:42 0:00 /usr/bin/php-cgi root 31147 0.0 0.8 191944 4676 ? S 23:42 0:00 /usr/bin/php-cgi root 31148 0.0 0.8 191944 4676 ? S 23:42 0:00 /usr/bin/php-cgi
We then kill the processes since we’ll write an init script which starts the spawner by default. First of all, we’re writing a simple shell script which makes the exact same call as above. Then we’re putting the init script below into /etc/init.d.
/usr/bin/php5-fcgi
#!/bin/sh /usr/bin/spawn-fcgi -f /usr/bin/php-cgi -a 127.0.0.1 -p 49232 -C 2 -P /var/run/fastcgi-php.pid
/etc/init.d/php5-fcgi
#!/bin/bash PHP_SCRIPT=/usr/bin/php5-fcgi RETVAL=0 case "$1" in start) echo "Starting fastcgi" $PHP_SCRIPT RETVAL=$? ;; stop) echo "Stopping fastcgi" killall -9 php-cgi RETVAL=$? ;; restart) echo "Restarting fastcgi" killall -9 php-cgi $PHP_SCRIPT RETVAL=$? ;; *) echo "Usage: php-fastcgi {start|stop|restart}" exit 1 ;; esac exit $RETVAL
Dont’ forget to make both scripts executable:
user@host: ~ $ sudo chmod +x /usr/bin/php5-fcgi user@host: ~ $ sudo chmod +x /etc/init.d/php5-fcgi
To start the spawner during boot we’re putting its init script into the default runlevel:
user@host: ~ $ sudo update-rc.d php5-fcgi defaults
With the appropriate nginx configuration we’d be able to serve PHP content now, but before we’ll proceed we get Ruby going.
Get Ruby going
Since the deb package of rubygems only installs version 1.2.0, we have to upgrade it to at least 1.3.1. This has to be done manually since a ‘gem update –system’ is disabled on Debian based distributions by default.
user@host: ~ $ wget http://rubyforge.org/frs/download.php/45905/rubygems-1.3.1.tgz user@host: ~ $ tar xzf rubygems-1.3.1.tgz user@host: ~ $ cd rubygems-1.3.1/ user@host: ~/rubygems-1.3.1 $ sudo ruby setup.rb
After these steps, a ‘gem –version’ should show that it’s upgraded to 1.3.1. So this has been done, we’re ready for installing passenger now:
sudo gem install passenger
That’ll do the job for the Ruby prerequisites. One more step is required before starting the passenger installer: creating directories.
user@host: ~ $ sudo mkdir /var/lib/nginx user@host: ~ $ sudo mkdir /var/lib/nginx/body user@host: ~ $ sudo mkdir /var/lib/nginx/proxy user@host: ~ $ sudo mkdir /var/lib/nginx/fastcgi
We’re now ready to install nginx, but rather than installing it via aptitude we’ll compile it manually since otherwise passenger wouldn’t work. We’ll therefore download the source and then run the passenger installer since it’ll compile nginx for us after answering a couple of questions.
user@host: ~ $ wget http://sysoev.ru/nginx/nginx-0.6.36.tar.gz user@host: ~ $ tar xzf nginx-0.6.36.tar.gz user@host: ~ $ sudo passenger-install-nginx-module
What now starts is an interactive dialogue which guides us through the passenger installation including the compilation of nginx and pcre as another requirement. After we’ve hit enter required software is being checked. If you’ve did the aptitude steps in the first place everything should be allright by now, but in case the installer bothers about something that’s missing, follow the steps provided by the installer. It’s self-explanatory.
One more step and we’re asked to choose between two install methods. We choose 2 since we want to provide additional compile arguments before doing the actual work.
When we’re asked to provide the full path of the nginx source code, we type it in.
/home/user/nginx-0.6.36
As the prefix we should rather choose /usr/local/nginx instead of /opt/nginx.
/usr/local/nginx
Now comes the important part since we’re able to provide additional arguments before the actual compilation begins. nginx is highly configurable and versatile, it depends on your environment what you want to achieve. You might want to take a look at the official nginx Wiki to get information about the different modules. For me the following arguments did a good job.
--user=www-data --group=www-data --sbin-path=/usr/sbin --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --pid-path=/var/run/nginx.pid --lock-path=/var/lock/nginx.lock --http-log-path=/var/log/nginx/access.log --http-client-body-temp-path=/var/lib/nginx/body --http-proxy-temp-path=/var/lib/nginx/proxy --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --with-http_ssl_module --with-http_dav_module --with-http_gzip_static_module --with-http_stub_status_module --with-openssl=/usr/lib --without-mail_pop3_module --without-mail_smtp_module --without-mail_imap_module
Copy’n'paste of the output above won’t work because of the line break. If you want to adopt the exact same arguments stick to the output below:
--conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --pid-path=/var/run/nginx.pid --lock-path=/var/lock/nginx.lock --http-log-path=/var/log/nginx/access.log --http-client-body-temp-path=/var/lib/nginx/body --http-proxy-temp-path=/var/lib/nginx/proxy --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --with-http_ssl_module --without-mail_pop3_module --without-mail_smtp_module --without-mail_imap_module --sbin-path=/usr/sbin --user=www-data --group=www-data --with-http_dav_module --with-http_gzip_static_module --with-http_stub_status_module --with-openssl=/usr/lib
Pushing enter once more and nginx is finally getting compiled and installed. This usually won’t take more than 5 minutes and should return no error. After this the passenger installation provides additional information which can be ignored by now since you’ll find the exact same directives in the configuration output below.
Get nginx going
The first thing we’ll write is, of course, an init script. Copy’n'paste the output below into /etc/init.d/ and make it executable.
#! /bin/sh # Description: Startup script for nginx webserver on Debian. Place in /etc/init.d and # run 'sudo update-rc.d nginx defaults', or use the appropriate command on your # distro. # # Author: Ryan Norbauer # Modified: Geoffrey Grosenbach http://topfunky.com set -e PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin DESC="nginx daemon" NAME=nginx DAEMON=/usr/sbin/$NAME CONFIGFILE=/etc/nginx/nginx.conf PIDFILE=/var/run/$NAME.pid SCRIPTNAME=/etc/init.d/$NAME # Gracefully exit if the package has been removed. test -x $DAEMON || exit 0 d_start() { $DAEMON -c $CONFIGFILE || echo -n " already running" } d_stop() { kill -QUIT `cat $PIDFILE` || echo -n " not running" } d_reload() { kill -HUP `cat $PIDFILE` || echo -n " can't reload" } case "$1" in start) echo -n "Starting $DESC: $NAME" d_start echo "." ;; stop) echo -n "Stopping $DESC: $NAME" d_stop echo "." ;; reload) echo -n "Reloading $DESC configuration..." d_reload echo "reloaded." ;; restart) echo -n "Restarting $DESC: $NAME" d_stop # One second might not be time enough for a daemon to stop, # if this happens, d_start will fail (and dpkg will break if # the package is being upgraded). Change the timeout if needed # be, or change d_stop to have start-stop-daemon use --retry. # Notice that using --retry slows down the shutdown process somewhat. sleep 5 d_start echo "." ;; *) echo "Usage: $SCRIPTNAME {start|stop|restart|force-reload}" >&2 exit 3 ;; esac exit 0
user@host: ~ $ sudo chmod +x /etc/init.d/nginx
Now let’s create the Debian-like directory structure under /etc/nginx for different vhosts. This enables us to activate and deactivate vhosts the easy way via symlinking.
user@host: ~ $ sudo mkdir /etc/nginx/sites-available/ user@host: ~ $ sudo mkdir /etc/nginx/sites-enabled/
What’s left is the actual configuration work of nginx. Please stick to the output below and make sure to read the comments with a leading #.
One short note: I’m not that familiar with all the aspects of nginx’s configuration yet, but the configuration below works just fine. In case you find any flaws or tweaks, I’d be glad to know about them.
/etc/init.d/nginx.conf
user www-data; worker_processes 1; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; sendfile on; #tcp_nopush on; #keepalive_timeout 0; keepalive_timeout 65; #gzip on; passenger_root /usr/lib/ruby/gems/1.8/gems/passenger-2.2.2; passenger_ruby /usr/bin/ruby1.8; include /etc/nginx/sites-enabled/*; }
/etc/init.d/sites-available/site-xyz
The following is an example for a virtual server with PHP support enabled, including various location directives, e.g. for the status page of nginx which is only available when the status module has been compiled. If you took my compile arguments above, don’t worry, it’s integrated.
server { listen 80; server_name www.example.com; access_log /var/www/example.com/logs/access.log; location / { root /var/www/example.com/htdocs; index index.php; # this serves static files that exist without running other rewrite tests if (-f $request_filename) { expires 30d; break; } # this sends all non-existing file or directory requests to index.php if (!-e $request_filename) { rewrite ^(.+)$ /index.php?q=$1 last; } } location ~ .php$ { fastcgi_pass 127.0.0.1:49232; #this must point to the socket spawn_fcgi is running on. fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME /var/www/example.com/htdocs$fastcgi_script_name; # same path as above fastcgi_param QUERY_STRING $query_string; fastcgi_param REQUEST_METHOD $request_method; fastcgi_param CONTENT_TYPE $content_type; fastcgi_param CONTENT_LENGTH $content_length; fastcgi_param SCRIPT_NAME $fastcgi_script_name; fastcgi_param REQUEST_URI $request_uri; fastcgi_param DOCUMENT_URI $document_uri; fastcgi_param DOCUMENT_ROOT $document_root; fastcgi_param SERVER_PROTOCOL $server_protocol; fastcgi_param GATEWAY_INTERFACE CGI/1.1; fastcgi_param SERVER_SOFTWARE nginx/$nginx_version; fastcgi_param REMOTE_ADDR $remote_addr; fastcgi_param REMOTE_PORT $remote_port; fastcgi_param SERVER_ADDR $server_addr; fastcgi_param SERVER_PORT $server_port; fastcgi_param SERVER_NAME $server_name; # required if PHP was built with --enable-force-cgi-redirect # fastcgi_param REDIRECT_STATUS 200; } # this location directive only works if compiled with the status module location /nginx_status { stub_status on; access_log off; allow all; } }
This is an example of a vhost configuration for a ruby powered vhost. As you can see the configuration here is much more straight-forward. Make sure that the root directive points to a directory of your project that’s called public.
server { listen 80; server_name www.example.com; access_log /var/www/example.com/logs/access.log root /var/www/example.com/htdocs/site-xyz/public; passenger_enabled on; }
Afterwards simply symlink the configuration from /sites-enabled to /sites-available and start nginx. If everything’s ok you may see nginx’s default website and be ready to add your own PHP and Ruby applications.
user@host: /etc/nginx/sites-enabled $ sudo ln -s /etc/nginx/sites-available/site-xyz site-xyz
If you’ve got questions or suggestions, don’t hesitate to start a discussion. I’m looking forward to improve this guide if it didn’t help you with the installation and configuration of nginx. The idea of using spawn_fcgi and the init script for it comes from ElasticDog.com, the init script for nginx has been written by Ryan Norbauer and modified by Geoffrey Grosenbach.
Hi – great topic and write-up! I’m trying to get this to work but when I finally execute
/etc/init.d/nginx startthen I get the error "[emerg]: bind() to 0.0.0.0:80 failed (98: Address already in use)".Any thoughts on what a possible solution could be to resolve this issue?
Thanks,
Torsten
Sorry for the late answer, Torsten. I was on holiday and not around any technical device except my phone! ;-)
“Address already in use” usually means that there’s already a webserver which is bound to the port you want to use for nginx. Give “sudo netstat -tulpe” a shot and see what blocks port 80 (quick guess: Apache). Either stop the Apache if possible or alter nginx’s server directive to port 8080 or any other port you prefer which is not being used on your server. And use a port above 1024 though since the ports below are reserved for common applications.
Let me know if I can be of further assistance.
Hi dear,
Thanks for your article. I think there is a typo in:
“/etc/init.d/nginx.conf” should read “/etc/nginx/nginx.conf”
Olivier
Great tutorial, although I’m getting an error when nginx is compiling. when it executes the make command, the following error occurs:
make -f objs/Makefile
make[1]: Entering directory `/usr/local/src/nginx-0.7.62′
cd /usr/lib \
&& make clean \
&& ./config –prefix=/usr/lib/openssl no-shared no-threads \
&& make \
&& make install
make[2]: Entering directory `/usr/lib’
make[2]: *** No rule to make target `clean’. Stop.
make[2]: Leaving directory `/usr/lib’
make[1]: *** [/usr/lib/openssl/include/openssl/ssl.h] Error 2
make[1]: Leaving directory `/usr/local/src/nginx-0.7.62′
make: *** [build] Error 2
libssl is in my usr/lib directory, but it doesn’t seem to notice it. I’m running this on ubuntu 9.04 64b vps. Any ideas?
Thanks in advance
thanks it works just fine.
but when I link the rails app at for example rails.domain.com it does the rails stuff and just domain.com gets me to php stuff but if I then type x.domain.com I always get to the rails app but shouldnt I get an error?! anyway probably just a simple vhost thing with nginx..