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.