Skip to main content

Capistrano and the custom maintenance page

·541 words·3 mins· ·
Capistrano Rails Git Migrations Deployment Maintenance
Ariejan de Vroom
Author
Ariejan de Vroom
Jack of all Trades, Professional Software Craftsman
Table of Contents

Randuin posted a comment on my previous Lighting fast, zero-downtime deployments with git, capistrano, nginx and Unicorn post asking how I handle database migrations. This is a good question.

Database migrations, especially with large datasets, take a long time to run. They also lock your database tables which may cause all kinds of trouble.

There’s a quite an easy solution for this, offered to us by Capistrano. Unfortunately it will cause downtime for your site while the migration is running.

~

Setup nginx
#

The first thing you should do it update your Nginx configuration in /etc/nginx/sites-available/default and add the following snippet, just before the location /:

if (-f $document_root/system/maintenance.html) {
    return 503;
}

error_page 503 @maintenance;
location @maintenance {
    rewrite  ^(.*)$  /system/maintenance.html last;
    break;
}

As iGEL and Anlek Consulting pointed out in the comments, it’s good practice to send a 503 Service Temporarily Unavailable HTTP code back to the client. A normal user won’t notice the difference, but spiders do. Sending the 503 code will tell search engines, like Google, that your site is not available and that they should not re-index your maintenance message as the new site content. Instead, they’ll come back later when your site returns a HTTP 200 code again.

You can try this fairly easily by putting your site in maintenance mode (per the instructions that follow) and do a HEAD request:

$ curl -I http://ariejan.net
HTTP/1.1 503 Service Temporarily Unavailable
Server: nginx/0.8.54
Date: Tue, 20 Sep 2011 18:22:35 GMT
Content-Type: text/html
Content-Length: 1276
Connection: keep-alive

Use Capistrano
#

Now, you can run (from your own machine):

cap deploy:web:disable

This task will upload a generic placeholder and place it in public/system/maintenance.html. If nginx sees this file exists, it will render it and abort further processing. So, as long as the maintenance.html file is present, your app is not accessible.

When you’re migrations are done, you can run:

cap deploy:web:enable

This will have remove the maintenance.html file, and thus making your app accessible again.

Piecing it all together
#

What you probably want is a separate task to deploy new code and run migrations. Here’s the task I used:

namespace :deploy do
  desc "Deploy and migrate the database - this will cause downtime during migrations"
  task :migrations do
    transaction do
      update_code
      web:disable
      migrate
       web:enable
    end
    restart
  end
end

Customize your maintenance page
#

Of course you want to customize the maintenance page, because, frankly, it’s kind of ugly by default. This does require you write your own deploy:web:disable task:

namespace :deploy do
  namespace :web do
    task :disable, :roles => :web, :except => { :no_release => true } do
      require 'erb'
      on_rollback { run "rm #{shared_path}/system/maintenance.html" }

      reason = ENV['REASON']
      deadline = ENV['UNTIL']

      template = File.read("./app/views/layouts/maintenance.html.erb")
      result = ERB.new(template).result(binding)

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

Now you can create a maintenance page for your app at app/views/layouts/maintentance.html.erb. See the original maintenance template for inspiration.

Note that you can use the environment variables REASON and UNTIL to give some info about why the app is down.

Keep in mind that when you put up the maintenance.html none of your files is accessible. This includes stylesheets and js files. Make sure to host any images and other assets on a remote server like Amazon S3.

Related

Lighting fast, zero-downtime deployments with git, capistrano, nginx and Unicorn
·2320 words·11 mins
Capistrano Rails Git Unicorn Nginx Zero-Downtime Deployment
Once and for all: Rails migrations integer :limit option
·99 words·1 min
MySQL Ruby on Rails Rails Sql Migrations Integer Limit
Speaking at Rails Underground
·111 words·1 min
General Ruby Rails Git Rails-Underground Speaking