Insights on Ruby, Git, jQuery, Cappuccino, WordPress, Debian and OS X. Please subscribe if you find something useful!

Dead Simple Rails Deployment

Posted: May 31st, 2009 | Author: Jerod | Filed under: Git | Tags: , , | Comments

Deploying a Rails app used to suck. Reverse proxies, Mongrel clusters, Monit, etc. Capistrano helped out a lot (once you set it up the first time), but all in all the process was still pretty painful.

Thankfully, a couple of technologies have come along and made my deployment process a whole lot easier.

  1. Passenger
  2. This was the big one. The Phusion guys’ “Hello World” app (as they called it) has really had a positive impact on the Rails community, and me personally. Suddenly my Rails (and Rack) web apps are first class citizens to Apache (and Nginx), which means I can just point a virtual host at the public directory and go. I had almost forgotten how good it feels to just drop some files in a directory and have Apache serve them.

  3. Git
  4. Ok, so maybe Subversion allows a similar workflow, but for some reason Git is one of those tools that is so much fun to use that it makes me think of different ways I can use it.

My Flow

How I deploy these days (when I’m not deploying to Heroku) is dead simple. I host my private Git repos using Gitosis, but the same would work with GitHub or any Git server.

Initial Setup

  1. Clone the repository on production server.
  2. Create database.yml and any other production-specific configs
  3. Configure an Apache virtual host pointing to “public” folder of the repository

Deploys

  1. locally:
    git push origin master
  2. remotely:
    git pull origin master && touch tmp/restart.txt

I know what you’re thinking, “Wow, that is dead simple”. It’s even easier by using Capistrano to execute the remote commands. Here is an example Capistrano task from one of my Rails apps:

task :deploy, :roles  => :production do
  system "git push origin master"
  cmd = [ "cd #{root_dir}", "git pull", "touch tmp/restart.txt" ]
  run cmd.join(" && ")
end

This task can be extended to automatically install required gems, update Git submodules, migrate the database, and so on.

Other Benefits

Besides the simplicity and ease of deployment in this process, I have also enjoyed the ability to make edits in production and pull them back in to my development environment. And because my production environment has a complete history of code changes, it is trivial to revert commits that cause major problems.

This work flow is by no means a panacea. How do you handle deployment?


  • vitaly
    you are missing out on a many 'features' of a traditional capistrano/vlad deployment setup.
    you can't easily revert to the previous version for example.
    Also your deployment is NOT atomic. A Rails requests coming during the deployment can fail etc.
  • Hi Vitaly-

    Wondering if you could explain the "not atomic" piece and tell me how a traditional cap deploy solves this.
  • With basic capistrano you would have a folder "releases" and a link "current" that links to the active realease within the release folder. So each time you deploy, a new release is created.

    So it first pulls the release from the repo and when this is done, it refreshes the link to the new release. In that way you can easily switch between releases in a blink of an eye. Whereas this one would possibly create errors during the "pull".
  • Correct me if I'm wrong, but won't Passenger continue to serve the "old" data (in production mode) until it is restarted? I have never received Rails errors during a pull, but the apps I'm deploying aren't sustaining constant requests either.
  • vitaly
    The production environment only caches code that is loaded.
    So if you go to a controller that was not yet visited since the last restart *during* another"pull in progress" you can get an error.

    Also consider static files. when you 'git pull' they do not change all at once, so you can have new html pointing to new images that are not yet there. etc.

    in short: its not atomic :)

    changing a symlink is.

    well, almost ..:) you can still serve some old html and once the request comes for the accompanying images or css files they can already be served from the new release. but thats another story...
blog comments powered by Disqus