Skip to content

Instantly share code, notes, and snippets.

@kix
Created May 29, 2012 07:18
Show Gist options
  • Save kix/2823087 to your computer and use it in GitHub Desktop.
Save kix/2823087 to your computer and use it in GitHub Desktop.
Capistrano multistage

Capistrano's multistage extension provides an easy way to use a different deployment strategy for different scenarios. For instance, for your application, you might have two servers: one for production, where the "live" code is, and one for staging, where you can test features out without risk of affecting anything critical. Certain deployment values, such as the application name, UNIX user and group, and application environment might be the same, but other values might be different, such as the location of the server itself or the SCM repository branch. Instead of repeating code in your Capfile, you can use multistage to set the values which are different in separate files, and only load those files at runtime.

This article will walk you through installing and using the multistage extension. We're going to use the example we mentioned earlier, where we have a production server and a staging server. So naturally, we would like two deployment stages, production and staging. We also assume you're creating an application from scratch.

Installation

The multistage extension used to live within a seperate capistrano-ext gem, but is built into capistrano in recent versions, although it's still namespaced as capistrano/ext/multistage.

So, let's start as usual by make a directory for our application and capify it:

mkdir ~/apps/capistrano-multistage-test
cd ~/apps/capistrano-multistage-test
capify .

Now open config/deploy.rb and replace it with this content:

set :stages, %w(production staging)
set :default_stage, "staging"
require 'capistrano/ext/multistage'

set :application, "capistrano-multistage-test"
set :user, "www-data"
set :group, "www-data"

set :scm, :git
set :repository, "ssh://ourserver/#{application}.git"
set :deploy_to, "/var/www/#{application}"
set :deploy_via, :remote_cache
set :deploy_env, 'production'

So what are we doing here? Well, first we tell Capistrano what our stages are. This will allow us to say cap production TASK or cap staging TASK to run a task within a specific stage. We also set the default stage to "staging", that way we can simply say cap TASK and it will execute the task within the "staging" stage by default. Then, we bring in the multistage code (yes, after we configure the stages -- it's just the way the extension works). Finally, we set some variables that will be common to our stages.

Let's also add a small task that we can use to test our stages:

task :uname do
  run "uname -a"
end

So that takes care of our main config file. What about the stages themselves?

Well, when you say cap my_stage TASK (for instance), Capistrano looks for a file config/deploy/my_stage.rb and loads it. It's in this file that you configure the variables and tasks that apply to that stage.

So first we need to create the directory that will house our stage configuration files:

mkdir config/deploy

Then we create two files, config/deploy/production.rb and config/deploy/staging.rb. In config/deploy/production.rb, we can do something like this:

server 'production.server.com', :app, :web, :primary => true

And in config/deploy/staging.rb we can do something like this:

server 'staging.server.com', :app, :web, :primary => true

That's it! If we were now to run cap production uname, the uname task would get executed on production.server.com; if we were to run cap staging uname, it would get run on staging.server.com. If we were to run simply cap uname, that would also happen on the staging server, since we set "staging" to the default earlier.

It's worth noting that anything we would normally put in our Capfile, we can also put in our stage files. So if, say, we were using Nginx + Unicorn on our production server but Apache on our staging server, we could override the deploy:restart task only for the production stage by putting this in config/deploy/production.rb:

namespace :deploy do
  task :start do
    # ...
  end
  
  task :stop do
    # ...
  end
  
  task :restart do
    # ...
  end
end

Caveats

Don't name your stage "stage", as this is a reserved word under the multistage extension (deploys won't do anything and in fact it will cause an infinite loop).

Alternatives

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment