I must not be the only one who occasionally wants to stop a Ruby on Rails application. There are long-running and risky database schema migrations and data migrations, and you don’t want users fiddling the system in the middle of the deployment.
With a Mongrel-based setup it was easy to set up a 503 (Service unavailable) error page and then just shut down all the Mongrels, so that Apache could give users a maintenance page. Using mod_rails (Phusion Passenger), only restart is supported out-of-the-box.
However, it’s possible to use mod_rewrite to prevent users from accessing your site during the deployment. Try this in your Apache virtual host configuration file:
# This option is not needed with Passenger >=2.1.1.
RailsAllowModRewrite on
ErrorDocument 503 /503.html
RewriteEngine on
RewriteCond %{DOCUMENT_ROOT}/../tmp/stop.txt -f
RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ /$1 [R=503,L]
Basically the last three lines mean:
- If there is a tmp/stop.txt file
- and user is requesting a file that does not directly exist in the file system
- then return error 503 and render the appropriate maintenance page
It doesn’t actually stop your Rails processes, though. They will die when Passenger times out.
Now it’s also easy to incorporate this to your Capistrano configuration:
namespace :passenger do
desc "Restart Passenger"
task :restart, :roles => :app do
run "touch #{current_path}/tmp/restart.txt"
end
desc "Stop Passenger"
task :stop, :roles => :app do
run "touch #{current_path}/tmp/stop.txt"
end
desc "Start (or un-stop) Passenger"
task :start, :roles => :app do
run "rm -f #{current_path}/tmp/stop.txt"
end
end
After this it’s safe to deploy big new releases without completely taking down your Apache.



I have used similar approach described in: http://clarkware.com/cgi/blosxom/2007/01/05/CustomMaintenancePages . In that approach the maintenance page is rendered with rails and then uploaded to the server. so instead of just stopping the server, you give capistrano the parameters that are displayed to the user in the maintenance page. E.g. ‘cap deploy:web:disable UNTIL=”five minutes” REASON=”publishing new features”‘. This is good approach because user needs to know when the maintenance started and when will it end.
Though, this post has better apache configuration. Returning 503 (what the other example didn’t do) and serving existing html (etc.) documents normally. So thanks for the hint :)
Nice… my solution was somehow more ugly… but this looks really nice. In combination with the previous comment one could create a nice setup.
There’s also a rack middleware for this: Rack::Backstage, it serves a static page if it exists (i.e. maintanance.html), otherwise it passes all requests down the line.
http://github.com/rack/rack-contrib/blob/c0cd4cee643d4b6c1756ad567290cdb992d829fc/lib/rack/contrib/backstage.rb
Am I missing something here? I never changed how I was handling the maintenance page when moving from mongrel to Passenger. The tried & true method of placing a maintenance page in system and symlinking it to public along with a few mod_rewrite rules still works for me.
This is a great suggestion–20 minutes later after reading it’s ready in production. Thanks.
Nice one. Perhaps it would be prudent to `rm stop.txt` in the `cap deploy:restart` task too. Otherwise if a `cap deploy:stop` was issued followed by a `cap deploy:restart`, the application wouldn’t start.
Thanks for that apache/capistratno snippet, comes in handy as a packaged deal :)
This worked totally awesomely! Thanks! (now we have a maintenance strategy that doesn’t suck! the guy that set up the old one was insistent that apache couldn’t serve images or css during a database maintenance window so we had a crappy looking maintenance page)