So we’ve installed ruby as described in my previous post. But we are nothing with only a ruby installation. To serve our ruby / rails apps, we’ll need a webserver. Until last year, I always used Apache to do the heavy lifting. But after some research, I decided to make the switch to Nginx. Either way, I’ll be using Nginx in combination with Passenger.
Essentials
Ok, first of all, you need to know that Nginx can’t load modules dynamically like Apache does. This means that you need to compile Nginx with all the options that you need before you can use them. So if you compile Nginx and need to activate a new module, you’ll need to recompile Nginx.
First start with installing a few essential tools for Nginx. In order to use the http rewrite-module, we need the PCRE library:
sudo aptitude install libpcre3 libpcre3-dev libpcrecpp0
Passenger
Before we install the passenger gem and start the installation process, we need to get the nginx source and another extra module I use.
I know that comes with a certain nginx install, but in most cases, isn’t the latest release. So it’s always good to have the latest release available.
wget http://nginx.org/download/nginx-1.0.14.tar.gz tar xvzf nginx-1.0.14.tar.gz
Second thing you need is to get the headers-more module. This module makes it possible to remove certain headers in the response that Nginx sends back. Personally, I don’t like headers containing software names and versions, certainly not in a production environment.
So lets download the extra module:
wget --no-check-certificate https://github.com/agentzh/headers-more-nginx-module/tarball/v0.16 -O headers-more.tar.gz tar xzvf headers.tar.gz
Now that all preparations are in place, we are ready to start the installation procedure:
sudo passenger-install-nginx-module
The first screen you see contains some information, so press enter to continue. The installer will start to check if all required software is available. So install it if the installer tells you you miss something.
After the check you get the 2 options: the first is an auto install. Since we are going to customize our installation, you’ll need option 2.
Next step you’ll be asked to specify the source directory where you untarred the nginx source code:
Please specify the directory: /home/michael/nginx-1.0.14
Now you’ll be asked to specify the installation directory:
Please specify a prefix directory [/opt/nginx]: /opt/nginx/nginx-1.0.14-p3.0.11
The installation directory might seem a bit odd. But let me explain it a bit in detail. Again, all self compiled software gets installed in the /opt directory. Since we want to group our nginx installs, we group them in the nginx folder.
The nginx-1.0.14-p3.0.11 folder contains the install itself. Here we are going to install nginx 1.0.14. The p3.0.11 that I appended at the end, stands for the passenger version. There are 2 reasons why I would need to recompile my nginx installation
- nginx got an update
- passenger got an update
So when I recompile nginx, I don’t want my running nginx installation to get corrupted, and all running processes to go haywire. So either way, even when nginx or passenger get updates, they will be installed in a new installation directory.
Final step is to give the extra compilation arguments:
--conf-path=/opt/nginx/shared/conf/nginx.conf --with-cpu-opt=amd64 --with-http_gzip_static_module --without-http_autoindex_module --without-http_browser_module --without-http_fastcgi_module --without-http_geo_module --without-http_empty_gif_module --without-http_map_module --without-http_ssi_module --without-http_userid_module --user=www-data --group=www-data --add-module='/home/michael/agentzh-headers-more-nginx-module-6cd7ae8'
I’m only going to explain the two most important arguments here:
- –conf-path : since we want to upgrade easily we’ll be placing our configuration files in a shared directory. This way there is no hastle with copying configuration files.
- –add-module: add the path to the extra header-more module, so we can remove some headers later on.
Now that the installation is finished, create a symlink pointing to the current nginx installation:
sudo ln -s /opt/nginx/nginx-1.0.14-p3.0.11 /opt/nginx/nginx
Configuration
My nginx configuration is pretty basic. I’ll document it inline where needed:
user www-data www-data; worker_processes 1; error_log /var/log/nginx/error.log; pid /var/run/nginx.pid; events { worker_connections 1024; } http { # passenger and ruby location passenger_root /opt/ruby/ruby/lib/ruby/gems/1.9.1/gems/passenger-3.0.11; passenger_ruby /opt/ruby/ruby/bin/ruby; passenger_max_pool_size 15; include mime.types; default_type application/octet-stream; access_log /var/log/nginx/access.log; sendfile on; keepalive_timeout 65; client_max_body_size 100M; # we set server tokens off to hide server information server_tokens off; # remove Server header from nginx and X-Powered-By / X-Runtime from passenger more_clear_headers 'Server' 'X-Powered-By' 'X-Runtime'; # I had to increase the server hash max size. Too much server names server_names_hash_max_size 1024; gzip on; gzip_comp_level 9; gzip_buffers 16 8k; gzip_disable "MSIE [1-6]\."; gzip_proxied any; gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript; charset utf-8; # We save all our vhosts in the shared folder where we also store the config files. Store them in /opt/nginx/shared/sites-available/ # This way, we symlink all enabled sites from sites-enabled. All enabled sites get included. In analogy to the Apache setup include /opt/nginx/shared/sites-enabled/*; }
Final step is saving the nginx startup script in the /etc/init.d directory:
#!/bin/sh ## # nginx start script ## PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/opt/ruby/ruby/bin DAEMON=/opt/nginx/nginx/sbin/nginx NAME=nginx DESC=nginx if [ ! -x $DAEMON ] then echo "Couldn't find $DAEMON. Please set path to DAEMON." exit 0 fi set -e case "$1" in start) echo -n "Starting $DESC: " start-stop-daemon --start --pidfile /var/run/$NAME.pid --exec $DAEMON -- $DAEMON_OPTS echo "$NAME." ;; stop) echo -n "Stopping $DESC: " start-stop-daemon --stop --pidfile /var/run/$NAME.pid --exec $DAEMON echo "$NAME." ;; restart|force-reload) echo -n "Restarting $DESC: " start-stop-daemon --stop --pidfile /var/run/$NAME.pid --exec $DAEMON sleep 1 start-stop-daemon --start --pidfile /var/run/$NAME.pid --exec $DAEMON -- $DAEMON_OPTS echo "$NAME." ;; reload) echo -n "Reloading $DESC configuration: " start-stop-daemon --stop --signal HUP --pidfile /var/run/$NAME.pid --exec $DAEMON echo "$NAME." ;; *) N=/etc/init.d/$NAME echo "Usage: $N {start|stop|restart|force-reload}" >&2 exit 1 ;; esac exit 0
Make sure the path and daemon paths are correct.
vhost
Last thing is a vhost example of a typical Ruby on Rails vhost. You can find extra info inline:
server { listen 80; server_name vhost.be; # create a separate access and error log per vhost access_log /var/log/nginx/vhost.www.log; error_log /var/log/nginx/vhos.www.error.log; location / { # vhost root for rails apps is the public folder and enable passenger root /srv/vhost/www/current/public; passenger_enabled on; # the following if statements is to check if the requests asks for a static file. If it does, # then it needs to be served by Nginx directly. # but if it doesn't exist, we want nginx to return a 404 error and not hit the Rails stack set $asset F; if ($request_uri ~* "^/(images|javascripts|stylesheets|flash|media|assets)/.*$") { set $asset T; } if (!-f $request_filename) { set $asset "${asset}T"; } if ($asset = TT) { return 404; break; } } # we want to compress the static files as much as possible and also cache the assets # this is needed for rails 3 location ~^/(assets)/ { root /srv/netronix/www/current/public; gzip_static on; # to serve pre-gzipped version expires max; add_header Last-Modified ""; add_header ETag ""; add_header Cache-Control public; break; } } # auto start passenger process when we restart nginx passenger_pre_start http://www.netronix.be;
So, by now you should have a fully operational Ruby and Nginx-Passenger stack. Hope everything worked out and if you have any suggestions, do let me know