Skip to content
This repository

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 separate 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 :rails_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 (of particular interest, we're setting rails_env to 'production' for all stages here - see the section on rails_env below for an alternate configuration).

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

rails_env

In the default deploy.rb file above, the variable rails_env is set to production in all stages. If you are deploying to a staging environment as one of your stages (let's call it staging), you may want to set rails_env differently in each stage. You can do this like so:

# in config/deploy/production.rb
set :rails_env, 'production'

# in config/deploy/staging.rb
set :rails_env, 'staging'

# in config/deploy.rb, use lazy evaluation to access variable set in stage-specific configs:
set(:deploy_to) { "/home/#{user}/web/#{rails_env}" }

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).
  • If you set variables within the stage-specific files config/deploy/my_stage.rb (e.g. rails_env as above) which you then want to refer to in the shared config/deploy.rb file, you need to use the lazy evaluation form of set, e.g.

    # in config/deploy/production.rb
    set :rails_env, 'production'
    
    # in config/deploy.rb
    set(:deploy_to) { "/home/#{user}/web/#{rails_env}" }
    

    and similarly for role if you are using that in the same way.

Alternatives

Something went wrong with that request. Please try again.