25

So I am using Capistrano to deploy a rails application to my production server (apache+passenger) and at the moment deployment usually goes along the lines:

$cap deploy
$cap deploy:migrations

It got me wondering, let's say my db:migrations took a long time to execute on the production server (a big refactor of the db schema) - in this case what is best practice with Capistrano? What happens if users are connected to my application at the time of deployment? Should I gracefully send users to a static placeholder page while the database is being updated? Does Capistrano handle this automagically? Do I need to code up a recipe to help with this? Or does the internal mechanisms of rails / passenger mean that I don't have to worry at all about this particular case?

Thanks.

John Topley
  • 113,588
  • 46
  • 195
  • 237
Clinton
  • 3,638
  • 3
  • 26
  • 33

2 Answers2

36

You should put up a maintenance page if the application is not going to be available for a while. I use this Capistrano task:

namespace :deploy do
  namespace :web do
    desc <<-DESC
      Present a maintenance page to visitors. Disables your application's web \
      interface by writing a "maintenance.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.

      By default, the maintenance page will just say the site is down for \
      "maintenance", and will be back "shortly", but you can customize the \
      page by specifying the REASON and UNTIL environment variables:

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

      Further customization will require that you write your own task.
    DESC
    task :disable, :roles => :web do
      require 'erb'
      on_rollback { run "rm #{shared_path}/system/maintenance.html" }

      reason = ENV['REASON']
      deadline = ENV['UNTIL']      
      template = File.read('app/views/admin/maintenance.html.erb')
      page = ERB.new(template).result(binding)

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

The app/views/admin/maintenance.html.erb file should contain:

<p>We’re currently offline for <%= reason ? reason : 'maintenance' %> as of <%= Time.now.utc.strftime('%H:%M %Z') %>.</p>
<p>Sorry for the inconvenience. We’ll be back <%= deadline ? "by #{deadline}" : 'shortly' %>.</p>

The final step is to configure the Apache virtual host with some directives to look for the maintenance.html file and redirect all requests to it if it's present:

<IfModule mod_rewrite.c>
  RewriteEngine On

  # Redirect all requests to the maintenance page if present
  RewriteCond %{REQUEST_URI} !\.(css|gif|jpg|png)$
  RewriteCond %{DOCUMENT_ROOT}/system/maintenance.html -f
  RewriteCond %{SCRIPT_FILENAME} !maintenance.html
  RewriteRule ^.*$ /system/maintenance.html [L]
</IfModule>

To put the application into maintenance mode, run cap deploy:web:disable and to make it live again do cap deploy:web:enable.

John Topley
  • 113,588
  • 46
  • 195
  • 237
  • 1
    Will the maintenance page be served with "200 OK" or "503 Service Temporarily Unavailable"? Google might think, your precious content might be gone, if you serve it with 200 OK. See http://googlewebmastercentral.blogspot.com/2011/01/how-to-deal-with-planned-site-downtime.html – iGEL Sep 20 '11 at 15:55
  • 1
    Good point, Capistrano itself suggest the following snippet for the .htaccess: https://gist.github.com/1292705 – Volker Rose Oct 17 '11 at 14:23
  • 1
    You forgot the `enable` task! – Jay Levitt Dec 16 '11 at 20:17
  • 1
    @Jay Levitt The `enable` task is built in to Capistrano itself, so there's no need to specify it explicitly unless overriding it. – John Topley Dec 17 '11 at 09:03
5

My production deploys generally follow this process:

  1. cap production deploy:web:disable which directs all requests to a static maintenance page
  2. cap production deploy
  3. migrations etc, testing each of the servers individually to make sure things are OK
  4. cap production deploy:web:enable to make the site work as it should

John Topley's response gives you some good in depth info here.

dylanfm
  • 6,292
  • 5
  • 28
  • 29