Capistrano + Rails + Bundler + RVM + Unicorn + EC2

Capistrano + Rails + Bundler + RVM + Unicorn + EC2In simple cases, if you use Heroku, application deployment process can be as easy as one shell command. But Heroku does not provide enough scaling and flexibility for more advanced scenarios or more serious load.

If you need to test something and then be able to expand to thousands of requests per second, EC2 from Amazon Web Services is definitely the way to go. It provides you with a virtual system which is totally under your control. You can add additional storage, move storage between servers and increase CPU/memory in almost real-time.

The downside, though, is that you have to setup the whole application infrastructure by yourself: from frontend servers to deployment scripts to security customizations. There is no preferable way of doing one thing or another, so here I’m offering what worked perfectly for me, and what I was not able to find while surfing the Internet for solutions.

Overview

The problem we’re solving is Rails app deployment on a remote Amazon EC2 instance with Capistrano.

I’m considering Capistrano as the core component of the solution. Capistrano is used for application deployment and is pretty much industry standard these days. You can find lots of deployment recipes for it on the Internet. With Capistrano we can deploy Rails applications on remote servers, pre-configure databases, precompile assets and do some other fancy stuff.

RVM is used to install the latest Ruby version on the machine no matter the packaging system used. It allows to keep multiple Ruby/Rails versions and enables easy switching between them.

Bundler is a part of Rails installation. Bundler offers very efficient gem version control and deployment system. It will be used with Capistrano to install the necessary gems on the target machine.

Unicorn is a high-performance Unix-like Rack application server. We will be using it as a backend system here. nginx acts as a frontend, but its configuration is out of scope for the current post.

Directory Structure & Users

The directory structure is important for the two reasons:

  1. It provides a skeleton for services configuration.
  2. It serves us to harden application security.

The user we will be deploying our app under is ec2-user (standard Amazon EC2 user). We will also be using sudo to drop current user credentials and start Rails application under rails user. rails user is not allowed to write in our app’s dir, which is a good additional security measure.

This is the final directory structure after  $ cap deploy:setup and  $ cap deploy commands have been run. You can see custom permissions on  log and  pids folders, as we want to allow unicorn (after doing $ sudo -u rails) to be able to write into them.

Unicorn

The unicorn configuration is pretty typical:

Gemfile

Capistrano

This is the most interesting part, as we glue all the components together:

The End

That’s pretty much it. I always believed that reading code is the best way to understand what the author wanted to say. So here you will find mostly real-world examples and scenarios, with little or no additional description. If you don’t like this format or finding a hard time understanding what I’m writing about, please feel free to give your feedback below.

Good luck and happy coding! Wink

17 thoughts on “Capistrano + Rails + Bundler + RVM + Unicorn + EC2

  1. Vasily Post author

    Thanks, Andreas Smile
    Yes, I’ll cover nginx and unicorn integration with several additional performance tweaks the next week. So stay tuned!

    Reply
    1. Vasily Post author

      What exactly did you want to see covered? I was using EC2 AMI Linux t.micro instance, which is pretty much like CentOS installed from scratch Smile

      Reply
      1. Ryan

        I also was hoping to see EC2 instances be auto created when deployed with Capistrano. That would help with the complete end to end process, similar to how Heroku pushes out new instances.

        Reply
  2. Peter Hicks (@poggs)

    I love the article! One question – servers get rebooted, and in this example as there’s no init script for Unicorn, it’ll need manual intervention.

    Do you have any pointers to a sensible init script that can manage Unicorn? I’ve tried here but I just can’t get the darned thing working reliably.

    Reply
    1. Vasily Post author

      Thanks, Peter!

      As for init scripts, I think there is no “right” and universal way of doing it. For example, I’ve seen several init.d script implementations which had very complex logic inside them to dynamically find all Rails apps and try to start them (as root!) during the bootup.

      I believe this approach is kind of unnecessary here. For my deployments, I typically just add one-liner in /etc/rc.local file:

      Of course, adding this line can be just another step in $ cap deploy:setup Capistrano scenario.

      Not very sexy, though, but pretty much reliable Wink

      Reply
  3. PunkOps (@oipunx)

    Here’s some of our Capistrano Recipes we’ve been using with SCALR to manage our EC2 instances.

    https://github.com/donnoman/scalr
    https://github.com/rickrussell/cap-recipes
    https://github.com/donnoman/cap-recipes

    https://github.com/donnoman/cap-recipes/blob/master/lib/cap_recipes/tasks/nginx_unicorn/install.rb

    We use god to manage nginx/unicorn/resque. In some instances we’ve needed Upstart to keep an eye on god, haha. But it works well for us. We’re in the process of moving to chef/cucumber-chef

    We use these in combination with the SCALR API and Jenkins to do our deploys. All you would need is a seperate repo with stages and set overrides.

    Reply

Leave a Reply