Generate mod_rails httpd.conf using cap

With all the discussions around the new mod_rails (aka Passenger), I thought I’d post the Apache conf that I came up with. Here’s the strategy: generate httpd.conf using cap, upload it to the server, and graceful restart Apache. This way, you don’t have to get your hands dirty logging into the box to reconfigure anything.

Here’s the erb template for httpd.conf:

#
# Apache httpd.conf
# generated by cap. run by <%= user %> at <%= Time.now %>
#

ServerRoot "<%= current_release %>"
PidFile "<%= current_release %>/tmp/pids/httpd.pid"
LockFile "<%= current_release %>/log/accept.lock"
ServerName localhost
ServerAdmin "me"
Listen <%= web_port %>

# prefork MPM
# StartServers: number of server processes to start
# MinSpareServers: minimum number of server processes which are kept spare
# MaxSpareServers: maximum number of server processes which are kept spare
# MaxClients: maximum number of server processes allowed at a time
# MaxRequestsPerChild: maximum number of requests a server process serves until death
StartServers          <%= apache_processes %>
MinSpareServers       <%= apache_processes %>
MaxSpareServers       <%= apache_processes %>
MaxClients            100
MaxRequestsPerChild   0

ErrorLog "<%= current_release %>/log/apache_error_log"
# Possible values include: debug, info, notice, warn, error, crit, alert, emerg
LogLevel info
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
CustomLog "<%= current_release %>/log/apache_access_log" combined
#RewriteLog "<%= current_release %>/log/apache_rewrite_log"
# 0-9, 0 = off, 2 and 3 is good for debugging
#RewriteLogLevel 3

ServerTokens Prod
ServerSignature Off

DefaultType text/plain
TypesConfig /usr/local/apache2/conf/mime.types
AddType application/x-compress .Z
AddType application/x-gzip .gz .tgz

<% if behind_https %>
# for envs that sit behind an https load balancer
RequestHeader set X-FORWARDED_PROTO https
<% end %>

# using Passenger (http://www.modrails.com/) to serve Rails
LoadModule passenger_module <%= gem_home %>/passenger-1.0.1/ext/apache2/mod_passenger.so
RailsSpawnServer            <%= gem_home %>/passenger-1.0.1/bin/passenger-spawn-server
RailsRuby             <%= ruby_binary %>

RailsUserSwitching    off
# max number of Rails processes to be spawned at a time
RailsMaxPoolSize      <%= max_rails_processes %>
# spawner processes will be killed if idle for 60 minutes
RailsPoolIdleTime     3600
RailsEnv              <%= environment %>

RailsAllowModRewrite  on
RewriteEngine On
# Check for maintenance file and redirect all requests
RewriteCond %{DOCUMENT_ROOT}/system/maintenance.html -f
RewriteCond %{SCRIPT_FILENAME} !maintenance.html
RewriteRule ^.*$ %{DOCUMENT_ROOT}/system/maintenance.html [L]

DocumentRoot "<%= current_release %>/public"
<Directory <%= current_release %>/public>
    Options FollowSymLinks
    AllowOverride None
    Order allow,deny
    Allow from all

    # Set the max size for file uploads to 10M
    LimitRequestBody 10485760
</Directory>

<Directory />
    Options FollowSymLinks
    AllowOverride None
    Order Deny,Allow
    Deny from all
</Directory>

Here is the cap task to generate httpd.conf and upload it to the server:

desc "Generate the apache httpd.conf file from the template"
task :generate_apache_httpd_conf_file, :roles => :app do
  require 'erb'
  # set up defaults for some vars to go into the template. others are required.
  apache_processes = fetch(:www_procs, 2)
  max_rails_processes = fetch(:max_rails_procs, 2)
  ruby_binary = fetch(:ruby_binary, "ruby")
  gem_home = fetch(:gem_home, '/usr/local/lib/ruby/gems/1.8/gems')
  behind_https = fetch(:fronted_by_https, false)
  template = ERB.new(File.read('config/templates/httpd.conf.erb'), nil, '<>')
  result = template.result(binding)
  put(result, "#{current_release}/httpd.conf")
end

It runs in an after_update_code task. This way, all server configs are under code control. This is huge! Each deployed environment’s settings are known and tracked - not just loosely keyed on each server. I treat Apache as part of our application - not a service that happens to be running that we deploy into. Using this technique, along with vendor_everything, we’ve minimized our host dependencies.