Perform a command serially in Capistrano

Capistrano is a great tool for doing software deployments and other system maintenance tasks (although for anything larger, I’d recommend Chef).  One small Capistrano issue I ran into this week is that there’s no way to run a command in serial across multiple machines (commands are always run in parallel). You might want to do this if you need to make sure that at least one service in a group is available at all times, or if the service restarting is going to put some load on another resource such as a database.

Here’s the solution I came up with:

desc "Do a rolling restart of <service>"
deploy.task :restart do
  hosts = self.roles[:server].to_ary # change :server to your role
  num_hosts = hosts.size
  hosts.each_with_index do |host, i|
    puts "Restarting <service> on #{host} (#{i+1} of #{num_hosts})"
    sudo "/etc/init.d/<sevice> restart", :hosts => host
    if i < num_hosts-1
      puts "Waiting 3s before next host."
      sleep(3)
    end
  end
end
  • Anonymous

    There is a :max_hosts => n parameter you can set on in task declaration to limit its maximum parallelism.

  • http://powdahound.com Garret Heaton

    How would you use that to deploy to all hosts though? I’m still deploying to every host, just serially.

  • http://mspo.com matt sporleder

    :max_hosts => n on a task does not apply to the -whole task-, it is like inherited by all of the individual jobs within the task.

    So even if you have
    task :foo, :max_hosts => 1
    run “hostname; sleep 5″
    run “date”
    end

    Each “run” will be executed serially on all hosts. The output of “date” will be the same.