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
- Install capistrano and capistrano multistage
- Capify your project
- Set up the
deploy/
directory - 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.
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!
@Marc You can alternatively put “RailsEnv staging” in public/.htaccess that is likely what the copy_htaccess task is for :)
Kris: exactly!
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}
^
What version of Ruby are you using? Do you need to use the Ruby 1.9 hash syntax?
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.
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.
I’m installed ruby 2.2.4 – p230, capistrano-3.4.0 and capistrano-ext-1.2.1 for this.
These instructions are for Capistrano 2. You have Capistrano 3. You need newer instructions. I’ve updated the article to indicate that it is now outdated.
Thanks for your reply.
So what is the newer instructions for Capistrano 3 ? Can you help me for new instructions?