Phusion Passenger Enterprise has excellent support for rolling restarts, also known as "zero-downtime restarts". This allows you to deploy new versions of your application without causing down time for your users.

Capistrano is a popular tool among Ruby developers for automating the deployment of new application releases. Setting up rolling restarts with Capistrano, in combination with Passenger Enterprise's Nginx mode or its Apache mode, is easy enough and works as expected.

But when using Capistrano with Passenger Enterprise's Standalone mode, rolling restarts appear to be broken. It seems that Passenger Enterprise doesn't recognize newly deployed releases. Why's that?

Problem

The problem is related to working directories and the way Capistrano sets up release directories. Whenever you deploy a new release, Capistrano sets up a new directory for that release. For example:

/var/www/yourapp/releases/201302080843
/var/www/yourapp/releases/201402191523
/var/www/yourapp/releases/201502201915

Capistrano also sets up a symlink named current, which always points to the latest release:

/var/www/yourapp/current
Points to -> /var/www/yourapp/releases/201502201915

Suppose that you have a restart task in your Capistrano script which tells Passenger Enterprise to restart the app after a deploy.

after 'deploy:publishing', 'deploy:restart'
namespace :deploy do
  task :restart do
    execute :touch, "tmp/restart.txt"
  end
end

This will not work! Passenger Standalone is still running inside one of the previous releases, say the 2014 release. If you touch restart.txt, then Passenger Standalone will restart the app inside the 2014 release, not one in the latest release.

The Nginx and the Apache mode doesn't suffer from this problem because they are not serving a specific release directory. Instead, they're serving the current symlink, which always points to the latest release. So we need to make the Standalone mode do the same.

Solution

The solution is to start Passenger Standalone outside any of the release directories, and to explicitly tell it to serve the application pointed to by the current symlink.

The normal (and problematic) way to start Passenger Standalone is to change to the application's directory and to run passenger start there:

# Wrong example. Doesn't work well with rolling restarts.
cd /var/www/yourapp/current && passenger start --rolling-restarts

But instead of doing this, you need to start Passenger Standalone outside the application directory. And at the same time you need to instruct Passenger Standalone to serve your application at the current symlink. Here's how you can do that:

# Correct example. Works well with rolling restarts.
cd /var/www/yourapp && passenger start current --rolling-restarts --pid-file shared/pids/passenger.pid --log-file shared/logs/passenger.log

Starting Passenger Standalone in /var/www/yourapp will cause it to write its log and PID file to that directory by default, but we don't really want those files there. That's why we pass the --pid-file and --log-file options to explicitly specify where we want those files. The shared directory created by Capistrano is an excellent location.

Once you've done this, touching restart.txt will work as expected.

Conclusion

There are some caveats associated with using rolling restarts with Passenger Enterprise Standalone. They're caused by working directories. But solving this is easy and only involves changing the Passenger Enterprise command invocation.