Unicorn is my favourite application server for Ruby web apps.
I have been running Unicorn in production since October 2009 and it has been faster, more reliable, and more predictable than any of the other options I’ve used.
Unicorn has been around for a while and hasn’t changed a whole lot, so it might seem like this post comes almost three years (unfashionably) late.1 However, there are a few reasons I felt it was relevant:
I still hear people say they are “thinking of checking out Unicorn”.
I frequently come across client projects and projects friends are working on which use Passenger, Thin, or something else for no reason in particular.2
I know people who actually use Unicorn but do not make use of great features like no-downtime restarts and seamless upgrades.
I want to give these people another nudge towards considering Unicorn. I wouldn’t bother if Unicorn was merely faster or easier to setup. Rather, it offers features that really give it an edge for building reliable web services that are easy to maintain. Unicorn is an absolute pleasure to run in production.
This isn’t the first time someone has urged you to try Unicorn. You know it’s out there and is probably as great as everyone says it is, but you have been using your current deployment stack for a while and it works well enough for you. Why bother investing in something new?
Every time (most) Heroku apps cutover to a new release there is a lag. I hate this. Most of the time the cutover is rather brief, but for some larger apps it can be quite lengthy. Passenger apparently has a facility to avoid this3 but I have never been able to get it to work reliably and have seen people resort to some pretty terrific hacks for large, slow-loading apps.
If this has ever bothered you, Unicorn offers a simple and reliable solution: start a brand new copy of your app alongside the old one and seamlessly switch over to it only once up and running.
You are running N application server processes, how are they managed? You probably use HAProxy or Nginx to load balance between the workers, but what about lifecycle management? Do you monitor your application processes independently? How do you kill and restart workers that are misbehaving? When you are pushing new code, how are old workers shut down? Are they allowed to finish processing their current requests?4
Unicorn has two features which really make it shine for worker management. First, it uses a master-worker process setup that is extremely reliable. Without going into detail, the master process maintains a heartbeat connection with each worker allowing workers to be very quickly killed and restarted when things go awry. Second, Unicorn exposes a signal handling system that provides very fine-grained control over worker management, especially around upgrades.
Even if you don’t have a long lag during deploys, how long does it take to upgrade your server?5 How about Ruby? What happens if a server or Ruby upgrade doesn’t go as planned? Is it quick and safe to switch back to your previous setup?
Due to the way that Unicorn’s upgrade process works, you can upgrade your application code, Unicorn itself, and even Ruby during a deploy. What’s more, if you have things setup properly, a deploy that swaps out all of those components does not have to be any different than a deploy that only upgrades code. I literally run
$ cap deploy regardless of what is getting upgraded during a deploy.6
If you want to give Unicorn a spin, Github’s post is probably the best place to start. Unicorn’s website is http://unicorn.bogomips.org and has a lot of great information, especially on configuring Unicorn in non-standard setups (I recommend reading most of the ALLCAPS files in the sidebar).
Depending on your production setup, Unicorn probably won’t do all of the wonderful things mentioned here right out of the box. You will have to configure it properly and there are a few key aspects of Unicorn upgrades that can be tricky. I have tripped on these myself and know many others who have also. I even know people who run Unicorn without using its upgrade facility because they fought with it for a while but could not get it to work properly. However, if you are prepared to invest some time, it is not all that bad and will pay huge dividends once you figure it out. I am actually in the process of writing a separate post focussing on the Unicorn upgrade process that will be published shortly.
Github made a great blog post about Unicorn in late 2009 that caused a lot of people (myself included) to check out Unicorn. The post contains a number of reasons why Github switched and also a lot more technical detail on what Unicorn is about and how to get going. If you have not yet read it, your time is probably better spent there than here. ↩
Now, there are certainly good reasons to use servers other than Unicorn. However, the projects I am referring to were not making use of features unavailable in Unicorn while at the same time suffering from problems that Unicorn nicely solves. ↩
Again, Passenger provides facilities for worker management. However I have experienced a lot of issues with Passenger worker processes running out of control (c.f. Debugging frozen applications in the Passenger docs), and new release cutovers that were less than smooth and seamless. ↩
If you are using Passenger with Nginx, upgrading Passenger requires you to recompile Nginx. It is probably safe to assume that most Passenger installs get upgraded as often as new servers are provisioned. ↩
I feel stupid even saying this, but don’t try doing this for the first time on a production server. It will most likely fail in a horrific fashion. First read up on Unicorn’s site and mailing list and be sure to test your upgrade process in an environment that is as close as possible to what you are running in production. ↩