Advanced server definitions in Capistrano

Advanced server definitions in Capistrano

What if you have several servers with different users, ssh-keys, and even port numbers? How to manage all this stuff flexibly ? This tutorial covers poorly documented Capistrano features for advanced servers and roles configuration. Many of them obtained via digging into Capistrano sources.

This article covers following Capistrano topics:

  • roles configuration
  • server configuration
  • The way HOSTS, ROLES, HOSTFILTER, HOSTROLEFILTER variables affect configuration
  • :roles and :hosts settings in the task and run methods
  • How to specify server settings with :hosts option?

Before we start lets create minimal recipe.

Bootstrap

Capistrano loads only two standard recipes by default:

Let’s create our own simple configuration from scratch. Before we choose good server names! :)
servers

Assume we have several servers with following DNS names:

  • mr-white.reservoir.dogs
  • mr-orange.reservoir.dogs
  • mr-blonde.reservoir.dogs

Lets give our servers some roles at config/deploy.rb:

Alternatively you may declare it with server option:

Command invocation

Capistrano has a simple recipe:

Let’s ask servers about current date:

Running a task

Let’s create dead simple task that will show the date. Add following to your config/deploy.rb:

Look for our newly created task in the tasks list:

Let’s play:

So with such configuration task will be executed on all defined servers.

But what if we want to run the task always only for specific role? Configure it as:

And it will be executed as expected:

Environment Variables

Capistrano reacts on four special environment variables that allow you to change server definitions temporary. Let’s read about it from capistrano help:

Let’s define task that should be executed on servers with :nerd role

HOSTS Variable

Let’s run task with following HOSTS:

So command is executed on explicitly specified servers.

But it’s not limited to hosts specified in configuration:

So HOSTS variable overrides server definitions.

ROLES Variable

Let’s run a task with another role:

So ROLES variable also overrides server definitions.

HOSTFILTER Variable

Hosts from HOSTFILTER variable are excluded from servers list for certain task:

So it filters servers, but does not override servers configured for task.

HOSTROLEFILTER Variable

Let’s try to filter servers with another role:

So it also excludes hosts from the servers list for certain task.

For example if we have following task configuration:

Then the command

will be executed only on servers that have :db and :www role.

So let’s summarize:

  • HOSTS and ROLES variables override server definitions.
  • HOSTFILTER and HOSTROLEFILTER variables filter servers.

Thus it’s safe to use HOSTFILTER and HOSTROLEFILTER variables.

Advanced Server Definitions

Running command with custom options

Probably you don’t know that run command accepts the same options as that task itself. So it’s possible to write complex things like this:

So date is executed on all servers. And uname and whoami only on corresponding servers.

Task :roles and :hosts options

If you’ve read Capistrano source code, you may find yet another nifty option called :hosts there.
How does it differ from :roles options? Firstly, :hosts option have higher priority over :roles. Secondary, you always create new server definition object that is not in global server list by specifying server via :hosts options. It means such server won’t be affected with tasks that does not have a role.
It’s really nice option because sometimes you want to execute some commands (notifications for example) on certain hosts that are actually not a target for the code delivery.

Specifying alternative server options

Sometimes you need to specify special server options like username, port, ssh key for each of your servers. And it’s pretty easy when your servers are defined globally:

alternatively you may declare it with server option:

Specifying some server options in task

There is no problem if you use :roles in your task or run method. But what about :hosts task/run options?
At the first look there is an issue because :hosts option is an array of servers name.
But there is a trick available! Look at Capistrano::ServerDefinition

So solution is simple:

So you know how to specify alternative :user and :port options. But what about another server definition options e.g. ssh_options ?

Specifying any server options in task

Do we need to hack find_servers to pass specific :ssh_options for server defined in task? No! recently we found simple solution without hacking: look at server_list_from

So we can pass in :hosts options array of ServerDefinition objects! So solution looks like this:

It is better to define servers as task/run options:

Capistrate the world!

We hope this article will help you to use Capistrano more efficiently.