Configuring Nginx and Unicorn
Posted on Jul 09 in General, Ruby on Rails, Scalability | (13) Comments
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.
worker_processes 1;
user nobody nogroup;
pid /tmp/nginx.pid;
error_log /tmp/nginx.error.log;
events {
worker_connections 1024; # increase if you have lots of clients
# Set this to on if you have more than 1 working processes
# This will allow only one child to watch the pollset and accept
# a connection to a socket
accept_mutex off; # "on" if nginx worker_processes > 1
}
http {
include mime.types;
default_type application/octet-stream;
access_log /tmp/nginx.access.log combined;
# This tells Nginx to ignore the contents of a file it is sending
# and uses the kernel sendfile instead
sendfile on;
# Set this to on if you have sendfile on
# It will prepend the HTTP response headers before
# calling sendfile()
tcp_nopush on;
# This disables the "Nagle buffering algorithm" (Nginx Docs)
# Good for websites that send a lot of small requests that
# don't need a response
tcp_nodelay off;
gzip on;
gzip_http_version 1.0;
gzip_proxied any;
gzip_min_length 500;
gzip_disable "MSIE [1-6]\.";
gzip_types text/plain text/html text/xml text/css
text/comma-separated-values
text/javascript application/x-javascript
application/atom+xml;
upstream unicorn_server {
# This is the socket we configured in unicorn.rb
server unix:/var/rails/testapp/tmp/sockets/unicorn.sock ยป
fail_timeout=0;
}
server {
listen 80;
client_max_body_size 4G;
server_name _;
keepalive_timeout 5;
# Location of our static files
root /var/rails/testapp/public;
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
# If you don't find the filename in the static files
# Then request it from the unicorn server
if (!-f $request_filename) {
proxy_pass http://unicorn_server;
break;
}
}
error_page 500 502 503 504 /500.html;
location = /500.html {
root /var/rails/testapp/public;
}
}
}
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.
Comments
[...] Sleekd » Configuring Nginx and Unicorn – good set of notes on getting nginx and unicorn talking [...]
great! thank you.
Excellent, good guide! Just what I was after
this worked exceptionally well. Wondering if you have moved this to /etc/init.d/xxxx for easier handling?
Thanks for the feedback. I didn’t write an init script for this but others did, you can check out this page.
using:
try_files $uri $uri/ @unicorn; (with location @unicorn being the proxy_pass)
would be better than using the if statement for unknown files.
This looks very simple
how would you go about having more than one app or virtual host with a unicorn process each?
Cheers.
[...] Configuring nginx and unicorn [...]
I dont know what’s wrong with the unicorn.rb file. Everything I uncommment the these lines:
pid “~gitprj/xxx/tmp/pids/unicorn.pid”
stderr_path “~/girprj/xxx/log/unicorn.stderr.log”
stdout_path “~/gitprj/xxx/log/unicorn.stdout.log”
I always get error of this:
/home/yubuntu/.rvm/gems/ruby-1.9.2-p290/gems/unicorn-4.1.1/lib/unicorn/http_server.rb:698:in `initialize’: No such file or directory – ~/gitprj/xxx/log/unicorn.stderr.log (Errno::ENOENT)
FYI, I’m using Ubuntu 11.40, RVM and rails 3.1.0 and I am a newbie in Linux environment.
Where should I put the config file? Also, should I have apt-gotten installed the nginx server?
I have the same question like Joseph Le Brech.,
Should we use an upstream statement for each virtual?
Thanks
I followed your instructions but I always get an unicorn error when using upgrade:
E, [2011-11-14T18:10:13.463457 #5925] ERROR — : old PID:4394 running with existing pid=/path_to_app/current/tmp/pids/unicorn.pid.oldbin, refusing rexec
Do you have an idea what todo with that?
Thx!
Gambo
sorry, the error wasn’t related to this guide
Leave a Comment