cap deploy:web:disable and Phusion Passenger (final update) 6

Posted by smoe Thu, 09 Oct 2008 20:17:00 GMT

This may still be useful to people in a shared hosting environment, but If you are administering your own server this hack is obsolete. As of Passenger 2.1.1 mod_rewrite is fully supported, or so I am told, I’ll try it out soon.
 
When you switch from mongrel to Passenger you will see many improvements. The web sites are faster, configuration is simpler, and deployment is easier. With the Passenger Apache module handling Ruby on Rails there are no separate application server processes, no proxy server calls, and you don’t need mod_rewrite for static content.
 
The only thing you lose are some Capistrano recipes: cap deploy:stop and cap deploy:web:disable will not work. I can live without deploy:stop. I don’t want my users to see "no response from server" or "upstream proxy server error" or any other error message that looks like something blew up. That’s why I like deploy:web:disable. I wish I never needed it but if something does go wrong, or a database needs to be reloaded or re indexed, deploy:web:disable is nice to have. The default maintenance.html page is a clean, simple message that shows you are in control. Problem is when you lose mod_rewrite you lose the mechanism that instructed Apache to skip the web app and show the maintenance page.
 
I’ve seen reports that you can set RailsAllowModRewrite on and only use mod_rewrite for the maintenance page, but it didn’t work for me. As far as I can tell Passenger and mod_rewrite just don’t work together. Passenger does a good job with static content so I don’t miss mod_rewrite, and it is still available to virtual hosts that are not Ruby on Rails applications.
 
What I came up with is a before_filter for the application controller. It’s not exactly the same thing. The application still needs to start up and serve the maintenance page, but if this is the first before filter there should be no database access and you can do almost anything you would do with a mod_rewrite disable.
 

To make it easy to copy around I put this mix-in module in lib/maintenance_mode.rb

module MaintenanceMode
protected
  def disabled?
    maintfile = RAILS_ROOT + "/public/system/maintenance.html"
    if FileTest::exist?(maintfile)
      send_file maintfile, :type => 'text/html; charset=utf-8', :disposition => 'inline'
    end
  end
end

And make these the first two lines in class ApplicationController, before any before_filters that might access the database.

  include MaintenanceMode
  before_filter :disabled?

And that’s all it takes to get cap deploy:web:disable to work again. This is the bit of code I put at the end of config/deploy.rb to replace start, stop and restart, and provide some handy Capistrano recipes for working with passenger.

namespace :deploy do
 desc "Restart Application"
 task :restart, :roles => :app do
   run "touch #{current_path}/tmp/restart.txt"
 end

 desc "Not used with Passenger"
 task :start, :roles => :app do
   # nothing
 end
 desc "Not used with Passenger"
 task :stop, :roles => :app do
   # nothing
 end
end

namespace :passenger do
 desc "passenger memory stats"
 task :memory, :roles => :app do
   run "passenger-memory-stats" do |channel, stream, data|
   puts data
 end

 desc "passenger general info"
 task :general, :roles => :app do
   run "sudo passenger-status"
 end
end

I think for my next Ruby on Rails post I’ll show a capistrano recipe for generating a custom maintenance.html page.

Comments

Leave a comment

  1. Avatar
    Scott Moe 5 days later:

    I know, this should have been first.

      describe "in maintenance mode" do
       before :all do
         system "cp public/maintenance.html public/system/maintenance.html"
       end
    
       it "should show a page" do
         get 'index'
         response.should be_success
       end
       it "should use send file" do
         @controller.should_receive(:send_file)
         get 'index'
       end
    
       after :all do
         system "rm public/system/maintenance.html"
       end
      end
    
  2. Avatar
    Chad 25 days later:

    cap deploy:web:disable works great on my out of the box passenger instance and capistrano 2.5.0

  3. Avatar
    Matthew about 1 month later:

    Instead of a before_filter, this might be a good place to use an around_filter? That way you could yield only if the maintenance page isn’t there, i.e.

    if FileTest::exist?(maintfile) send_file maintfile, … else yield end

    Unlike Chad, I’ve tried cap deploy:web:disable with Passenger and it didn’t work for me.

  4. Avatar
    Erik Ostrom about 1 month later:

    The discrepancy might be because the RewriteRule has to be outside of a Directory - and, I suspect, outside of .htaccess, which is the only place I can put it on my shared hosting site. Looks like I’ll be trying MaintenanceMode in the morning….

  5. Avatar
    gbs@panix.com 3 months later:

    So I’ve come up with a much easier solution that doesn’t require your rails app to be running to work. I’ve simply created a separate apache virtual host configuration file for a ‘disabled’ version of the application which simply redirects all requests that aren’t for an image or a stylesheet or a javascript file to a single html file, like this:

    <VirtualHost *:80>
      ServerName myserver.org
      DocumentRoot "/opt/apps/myapp/current/public"
        AliasMatch (^/stylesheets/.*) "/opt/apps/myapp/current/public$1"
        AliasMatch (^/javascripts/.*) "/opt/apps/myapp/current/public$1"
        AliasMatch (^/images/.*) "/opt/apps/myapp/current/public$1"
        AliasMatch .* "/opt/apps/myapp/passenger/disabled.html"
      <directory "/opt/apps/myapp/current/public">
        Order allow,deny
        Allow from all
      </directory>
    </VirtualHost>
    

    Then I can simply have a reference in my apache2.conf file to a placeholder vhost file, which is a symbolic link to either the disabled or enabled version of the vhost file.

    A package installed apache2 server on ubuntu 8.04 is already set up to read any file in /etc/apache2/sites-enabled folder. So assuming that I’ve added the actual vhost files to #{deploy_to}/passenger, then I simply can simply add the symbolic links there with these tasks:

    task :setup_enabled_vhost, :roles => :passenger do
            sudo "rm #{passenger_vhost_dir}/#{application}_disabled"
            sudo "ln -sf #{deploy_to}/passenger/apache_vhost #{passenger_vhost_dir}/#{application}"
    end
    
    task :setup_disabled_vhost, :roles => :passenger do
            sudo "rm #{passenger_vhost_dir}/#{application}"
            sudo "ln -sf #{deploy_to}/passenger/apache_vhost_disabled #{passenger_vhost_dir}/#{application}_disabled"
    end
          
    desc "Enable the application"
    task :enable_app do
            setup_enabled_vhost
            restart_apache
    end
    
    desc "Disable the application and put up a 'site down for maintenance' page"
    task :disable_app do
            setup_disabled_vhost
            restart_apache
    end
    

    I use deprec which I’m modifying to set up all this stuff (and a whole lot more) automatically. My fork of it is at: http://github.com/zippy/deprec/tree/master

    Enjoy!

  6. Avatar
    proxy sites about 1 year later:

    Going to play with it, right away. Will post updates. Cool bit.

Comments