Disabling Your Web Site During Maintenance

Problem

You occasionally need to stop your Rails application while performing work on your server. Whether this maintenance is planned or not, you want to have a system in place for gracefully disabling your site until you put your application back online.

Solution

Capistrano has two default tasks called disable_web and enable_web. These tasks are designed to disable your Rails application temporarily, redirecting all requests to an HTML page explaining that the site is temporarily down for maintenance. Running:

$ cap disable_web

writes a file named maintenance.html to the shared/system directory created by Capistrano. A symbolic link is then created in the public directory of the running Rails application. For example, an application called cookbook located in /var/www/cookbook, will get a symbolic link in /var/www/cookbook/current/public:

system -> /var/www/cookbook/shared/system

The corresponding task, enable_web, simply deletes maintenance.html from the shared/system directory. For example:

$ cap enable_web

Capistrano doesn't do anything to redirect requests to maintenance.html. It's expected that your web server will be configured to detect the presence of this file and redirect all requests to it, if it exists. Otherwise, requests should be routed to your Rails application as they normally are.

If you're running Apache (and these tasks assume you are), you can use the mod_rewrite module to redirect requests based on the existence of maintenance.html. To do this, add the following to your main Apache configuration (or to the specific virtual host block if applicable):

DocumentRoot /var/www/cookbook/current/public RewriteEngine On RewriteCond %{DOCUMENT_ROOT}/system/maintenance.html -f RewriteCond %{SCRIPT_FILENAME} !maintenance.html RewriteRule ^.*$ /system/maintenance.html [R] 

Of course, DocumentRoot should point to the current/public directory of your Rails application.

Two options that you can set when calling disable_web are the reason for the down time and the date/time that user should expect the application to come back online. Set these as environment variables before calling cap disable_web, such as:

$ export REASON="a MySQL upgrade"
$ export UNTIL="Sat Jul 30 15:20:21 PDT 2006"
$ cap disable_web

Discussion

If you don't have mod_rewrite installed, you can configure it by recompiling Apache with:

$ ./configure --prefix=/usr/local \
> --enable-proxy=shared \
> --enable-proxy_http=shared \
> --enable-proxy-balancer=shared \
> --enable-rewrite

followed by:

$ make
$ sudo make install

After running cap disable_web from the setup described in the solution, users attempting to view any area of your site will see something like .

Figure 13-3. The default server maintenance page provided by Capistrano

You can also modify the template that's used to generate maintenance.html by editing capistrano-1.1.0/lib/capistrano/recipes/templates/maintenance.rhtml in your system's gem directory (e.g., /usr/local/lib/ruby/gems/1.8/gems/).

If you're running Lighttpd, you won't have the luxury of Apache's mod_rewrite conditions to test for the existence of maintenance.html. Instead, Lighttpd users typically have two configuration files: one for normal conditions, and another that redirects requests to system/maintenance.html. When cap disable_web is called, Lighttpd is stopped and then started with a shell script that specifies the maintenance configuration (e.g., lighttpd-maint.conf). When the application is brought back online, with cap disable_web, Lighttpd is again stopped and restarted using the normal lighttpd.conf configuration file.

This kind of functionality is easily added to default tasks with "extension" tasks, such as:

desc "Restart lighttpd with the lighttpd-maint.conf file"
task :after_disable_web, :roles => :web do
 run "/etc/lighttpd/stop-lighttpd.sh"
 run "/etc/lighttpd/start-lighttpd-maint.sh"
end desc "Restart lighttpd with the lighttpd.conf file"
task :before_enable_web, :roles => :web do
 run "/etc/lighttpd/stop-lighttpd.sh"
 run "/etc/lighttpd/start-lighttpd.sh"
end

start-lighttpd-maint.sh contains a command to start Lighttpd with the configuration file to be used, specified using the -f option:

start-lighttpd-maint.sh:

#!/bin/sh
/usr/local/sbin/lighttpd -f /etc/lighttpd/lighttpd-maint.conf

Here, after_disable_web is run after the disable_web task, and before_enable_web is run before enable_web. Before every Capistrano task is executed, any tasks that exist with the same task name, preceded by before_, are executed first. Similarly, tasks with names ending with _after are executed after the tasks that they correspond to.

See Also