HOWTO setup multistage deployment with Capistrano

These instructions are outdated. They were written for Capistrano 2.x. As of March 2016, Capistrano 3.x is current.

When I wrote about deploying PHP sites with Capistrano, step 6 was a bit of a throwaway — hey, here's a quick hack for handling multistage deployment. I'd been using variants of that technique for quite some time but figured there must be a better way. There is: Capistrano multistage. There is even documentation for this. But it misses a few steps.

How to setup multistage deployment with Capistrano

  1. Install capistrano and capistrano multistage
  2. Capify your project
  3. Set up the deploy/ directory
  4. Create the deployment recipes

In my Rails-based example, we'll have two stages: staging and production. You can name your stages whatever you want. However, don’t name one of your stages stage — that's a reserved word. Call it staging.

Install capistrano and capistrano multistage

gem install capistrano
gem install capistrano-ext

Capify your project

capify .

Set up the deploy directory

mkdir config/deploy
touch config/deploy/staging.rb
touch config/deploy/production.rb

Create the deployment recipes

You’re going to edit three files: deploy.rb, staging.rb and production.rb:

deploy.rb

set :stages, %w(staging production) set :default_stage, "production" require 'capistrano/ext/multistage'
default_run_options[:pty] = true
set :application, "myproject" set :use_sudo, false set :keep_releases, 5
set :repository, "https://somewhere/svn/myproject/trunk" set :scm, :subversion set :scm_username, "me" set :deploy_via, :export
# this is useful in a shared hosting environment, where you have your own JAVA_HOME or GEM_HOME. # otherwise, just set RAILS_ENV set(:rake) { "JAVA_HOME=#{java_home} GEM_HOME=#{gem_home} RAILS_ENV=#{rails_env} /usr/bin/env rake" }
# since :domain is defined in another file (staging.rb and production.rb), # we need to delay its assignment until they're loaded set(:domain) { "#{domain}" } role(:web) { domain } role(:app) { domain } role(:db, :primary => true) { domain }
namespace :deploy do task :start do ; end task :stop do ; end task :restart, :roles => :app, :except => { :no_release => true } do run "#{try_sudo} touch #{File.join(current_path,'tmp','restart.txt')}" end end
# for some reason, this isn't enabled by default after "deploy:update", "deploy:cleanup"
# here is an example task which uses rake, as defined above after "deploy:migrate", "load_sample_fixtures" desc "load sample fixtures" task :load_sample_fixtures do run "cd #{current_release}; FIXTURES=samples #{rake} db:fixtures:load" end

staging.rb

set :deploy_to, "/path/to/#{application}-stage" # My Rails app uses RJB, so it needs to know where Java lives set :java_home, "/path/to/java-6-openjdk" set :domain, "testing.myserver.ca" set :user, "paul" set :rails_env, "staging" # I am root on my staging server and have all the right gems installed # so I don't need GEM_HOME to be overridden set :gem_home, nil

production.rb

set :deploy_to, "/home/myuser/#{application}" set :java_home, "/home/myuser/sw/jdk" set :domain, "www.myserver.ca" set :user, "myuser" set :rails_env, "production" set :gem_home, "/home/myuser/ruby/gems/"
# in a shared hosting environment, you often need to specify your own passenger configuration desc "copy the .htaccess file (passenger configuration); setup_load_paths.rb (sets GEM_HOME)" namespace :deploy do task :copy_htaccess do run "cp #{current_release}/config/htaccess_production #{current_release}/public/.htaccess" run "mv #{current_release}/config/production_setup_load_paths.rb #{current_release}/config/setup_load_paths.rb" end end
after "deploy:update_code", "deploy:copy_htaccess"

Now, you’re set. You can deploy with cap deploy (for the default stage, production), or cap staging deploy. TextMate's Capistrano plugin handles this gracefully, presenting you with a dialog listing the available stages.

Join the Conversation

11 Comments

  1. Hi Paul,
    Thanks for the post, really useful. It would have been perfect for my if I didn’t spend an hour figuring out why it was running the staging server in production env. I found somewhere else that the apache config needed “RailsEnv staging” within the VirtualHost.
    Thought it might be useful to others.
    Thanks again!

  2. I seem to be having issues with the “:primary => true” part of the :db role. My deploy.rb file contains the following…
    (destination is set in the miltistage files)


    set (:domain) {"#{destination}.#{application}.com"}
    role (:app) {domain}
    role (:web) {domain}
    role (:db, :primary => true) {domain}

    And I’m getting the following error…


    /Users/sjf/.rvm/gems/ruby-1.9.3-p194/gems/capistrano-2.13.4/lib/capistrano/configuration/loading.rb:93:in `instance_eval': ./config/deploy.rb:25: syntax error, unexpected ',', expecting ')' (SyntaxError)
    role (:db, :primary => true) {domain}
    ^

  3. ruby 1.9.3p194 (2012-04-20 revision 35410) [x86_64-darwin11.4.0]

    Turns out this works…

    role :db, :primary => true do
    domain
    end

    It doesn’t matter if I use “:primary => true” or “primary: true” it behaves the same. However, as soon as I put the parens around “:db, :primary => true” or replace do/end with {} the syntax fails.

  4. Hi,
    After execute ‘cap staging deploy’ I am getting the following error:
    capistrano/ext/multistage requires Capistrano 2

    why it happening? please help me regarding this.

Leave a comment

Your email address will not be published. Required fields are marked *