Maintenance Page with Apache and Capistrano

Alberto Vena

29 Sept 2011 Development, Ruby On Rails

Alberto Vena

3 mins
Capistrano, down for maintenance splash screen

Sometimes our apps have to remain offline for maintenance purposes, sometimes for a short time, sometimes for more than a while. In both cases it's a good practice to show a maintenance page for several reasons:

  • users are aware of discomfort and they know when the website will return online;
  • search engines will not index our error pages because the response code associated with them will be 503 (Service Temporarily Unavailable);
  • it will prevent some users to create exceptions when requesting a resource that is being updated.

Configuring Apache

The first thing we have to do is configure Apache to force it to show this page for every request arriving to web server. To avoid manually changing apache's configuration every time we want to disable a website, we can write some conditions to show the maintenance page only if an html file is present in a specific folder.

Here it is the code to add in your Virtual Host (usually located in /etc/apache2/site-enabled/000-default )

<VirtualHost *:80>
  ...

  RewriteEngine On

  # Show maintenance page if it exists
  ErrorDocument 503 /system/maintenance.html
  RewriteCond %{REQUEST_URI} !\.(css|gif|jpg|png)$
  RewriteCond %{DOCUMENT_ROOT}/system/maintenance.html -f
  RewriteCond %{SCRIPT_FILENAME} !maintenance.html
  RewriteRule ^.*$  -  [redirect=503,last]

  ...
</VirtualHost>

We have to reload the web server to make changes happen:

$ sudo service apache2 reload

Now, if we create the file /system/maintenance.html, every requests will be redirected to this page with a 503 response code. Deleting this file the website will return fully available.

Note 1: Look at the first condition:

RewriteCond %{REQUEST_URI} !\.(css|gif|jpg|png)$

This ensures that stylesheets and images are served. In this way we can add this resources to our maintenance page.

Note 2: mod_rewrite has to be enabled. To do this run:

$ sudo a2enmod rewrite

Disable website with Capistrano

Manually moving this file on the server every time we want our website to be in maintenance mode could become a very tedious task. It's Capistrano's turn to accomplish this demand with a custom task:

$ cap deploy:web:disable

This task will create an html file in your local machine and it will move this file for us to the server. If we want, we can specify some useful arguments: the reason of maintenance and when the website will come back online:

$ cap deploy:web:disable \\
              REASON="hardware upgrade" \\
              UNTIL="12pm Central Time"

we'll obtain the following result:

Standard Maintenance Page

This is a link to the default maintenance page created by Capistrano.

Customizing Capistrano's maintenance page

It's impossible to deny that the default Capistrano maintenance page is as useful as "simple". If we have special graphic needs, the only one way to customize this page is overwrite deploy:web:disable task, specifying where our new custom page is located at.

Here it is an example using erb templates (Ruby on Rails default):

namespace :deploy do
  namespace :web do
    desc <<-DESC
        Present a maintenance page to visitors. Disables your application's web \
        interface by writing a "#{maintenance_basename}.html" file to each web server. The \
        servers must be configured to detect the presence of this file, and if \
        it is present, always display it instead of performing the request.

        Customized task allow us to render erb file. Usage:

        $ cap deploy:web:disable \\
                REASON="hardware upgrade" \\
                UNTIL="12pm Central Time"

    DESC
    task :disable, :roles => :app do

      on_rollback { rm "#{shared_path}/system/maintenance.html" }

      require 'erb'
      deadline, reason = ENV['UNTIL'], ENV['REASON']
      maintenance = ERB.new(File.read("./app/views/layouts/maintenance.html.erb")).result(binding)

      put maintenance, "#{shared_path}/system/maintenance.html", :mode => 0644
    end
  end
end

The last thing to do is to create a file (/app/views/layouts/maintenance.html.erb), printing somewhere the variables passed. For example:

<p style="color: red; font-size: 16px; line-height: 20px;">
  The system is down for <%= reason ? reason : "maintenance" %>
  as of <%= Time.now.strftime("%H:%M %Z") %>.
</p>
<p style="color: #666;">
  It'll be back <%= deadline ? deadline : "shortly" %>.
</p>

Disable website during each deploy

We can also disable website during each deploy. To do this we just can add a couple of custom hooks to our deploy.rb file:

before 'deploy:update_code', 'deploy:web:disable'
after 'deploy:restart', 'deploy:web:enable'

Other resources

You may also like

Let’s redefine
eCommerce together.