I first heard about Unicorn in an interview with 37Signal’s server admin Mark Imbriaco and it made me really curious. There are a few great resources explaining how Unicorn works and a neat benchmark comparing Mongrel, Passenger and Unicorn. I will share my experience playing with Nginx and Unicorn on a Debian Lenny box.
Assuming you have your Rails stack configured (I am running Ruby 1.8.7 on Rails 2.3.8) you can use RubyGems to install unicorn :
debian:/# gem install unicorn
We can check if everything went smooth by checking the version:
debian:/# unicorn -v unicorn v1.0.0
There are two binaries: unicorn and unicorn_rails. Executing them with -h parameter seems to produce the same options but as Tyler Bird mentions in his article, unicorn_rails config options are inspired by the script/server behavior and accepts options like -E for specifying the Rails environment.
As the guys from Github point out, Nginx -> Unicorn is a good alternative to Nginx -> HA Proxy -> Other backends (Mongrel, Thin, Passenger). The role of HA Proxy behind Nginx was to health check the backend cluster and make sure it is ready to connections. What’s nice about Unicorn is that the backend cluster (worker processes) are pulling requests from a shared socket as opposed to being pushed requests from a load balancer.
In short, what I will show you is how to create a Rails test app, create a unicorn.rb configuration file and configure Nginx to communicate with the Unicorn socket.
Let’s create the test app in the /var/rails folder:
debian:/var/rails# rails testapp
Next we create the unicorn.rb file in our /testapp/config folder. Most of the options I used are found in the sample unicorn.rb configuration file on Unicorn’s website. Here’s the unicorn.rb file as it applies to my setup:
worker_processes 2 working_directory "/var/rails/testapp/" # This loads the application in the master process before forking # worker processes # Read more about it here: # http://unicorn.bogomips.org/Unicorn/Configurator.html preload_app true timeout 30 # This is where we specify the socket. # We will point the upstream Nginx module to this socket later on listen "/var/rails/testapp/tmp/sockets/unicorn.sock", :backlog => 64 pid "/var/rails/testapp/tmp/pids/unicorn.pid" # Set the path of the log files inside the log folder of the testapp stderr_path "/var/rails/testapp/log/unicorn.stderr.log" stdout_path "/var/rails/testapp/log/unicorn.stdout.log" before_fork do |server, worker| # This option works in together with preload_app true setting # What is does is prevent the master process from holding # the database connection defined?(ActiveRecord::Base) and ActiveRecord::Base.connection.disconnect! end after_fork do |server, worker| # Here we are establishing the connection after forking worker # processes defined?(ActiveRecord::Base) and ActiveRecord::Base.establish_connection end
Next we are going to configure Nginx. My setup is really simple – normally you would have those settings configured on a virtual host but for the sake of this example I will add all the configurations in the main nginx.conf file.
As with the unicorn configuration, you should check out the sample nginx.conf file on Unicorn’s website, which is almost identical to what I am presenting here.
This is it. Now we restart Nginx to apply the settings and start unicorn from our /var/rails/testapp folder. Notice I added the -D parameter to daemonize the process. You can start it without it if you want to see how unicorn processes the requests live.
debian:/var/rails/testapp$ unicorn_rails -c config/unicorn.rb -D
Now open your browser or use links and navigate to http://localhost. You should see the Rails welcome page.