Ruby on Rails via Apache

I have decided (for a number of reasons) that the only acceptable way for ME to run rails is within my existing Apache 2.0 setup. For one things on my server I am already running Apache with a couple of virtual hosts. I suppose the choice boils down to facing the issues migrating to lighttpd or facing the issues with Apache and fastcgi, and I am choosing the latter. I am running Apache 2.0.54 and have no intention whatsoever of running Apache 1.3 !

I should note, based on some comments I see others making, that I have SELinux turned off entirely. You may have a whole set of issues if you have SELinux on.

To get set up to use the mod_fcgid flavor of fastcgi (which is claimed to actually work with Apache 2.0), you have to tangle with three separate packages! There is mod_fcgid itself, the fastcgi development kit, and ruby_fcgi.

mod_fcgid with fedora core 4 and 5

The comment on page 459 of the rails book is too good to resist: "With FastCGI, you're strapping a rocket engine on Rails".
Let's do it!

I am right in the midst of migrating from fedora core 4 to 5 (it is mid July, 2006 as I write this).
I intend to test fly this on core 4, but rather quickly move it to core 5. Here we go, first some links that look helpful:

Reading the above notes, one definitely gets the feeling he is heading out onto the bleeding edge here (and indeed you are!).

Building mod_fcgid

I got the mod_fcgid source as mod_fcgid.1.10.tar.gz from fastcgi.coremail.cn mod_fcgid download.
The first issue that comes up is that you are required to edit the Makefile and tell it where your apache install is.

A peek using rpm -qa | grep httpd on my system shows that I have both httpd and httpd-devel installed, as per version 2.0.54, this seems good. And looking at the Makefile, I find that it will be looking for the following: include ${top_builddir}/build/special.mk

locate finds this at: /usr/lib/httpd/build/special.mk so my bet is to tell it that my apache is at /usr/lib/httpd, (it turns out this is correct).

tar xzvf mod_fcgid.1.10.tar.gz
cd mod_fcgid.1.10
-- edit Makefile
make
su
make install

The make goes smooth, so I must have the appropriate dependencies. Among perhaps other things, this puts mod_fcgid.so into /usr/lib/httpd/modules. Cool, easy so far.

The fastcgi development kit

I have to inject a comment here. There is something basically and fundamentally wrong with this step. We are using mod_fcgid, not fastcgi, yet we are going to build ruby_fcgi against the fastcgi headers. Maybe these two packages are compatible on a level that makes all of this work, but it gives me the heebie-jeebies.

You need to do this before trying to install ruby_fcgi (as I found out). If you just jump in and try to gem install fcgi at this stage, it will flounder in the directory /usr/lib/ruby/gems/1.8/gems/fcgi-0.8.6.1/ext trying to find fastcgi/fcgiapp.h. I download fcgi-2.4.0.tar.gz from www.fastcgi.com. Then:

tar xzvf fcgi-2.4.0.tar.gz
cd fcgi-2.4.0
configure
make
make install
This all goes smoothly (ignoring all the apparently harmless compile errors), and the make install goes into /usr/local, so I don't even need to su on my machine. In particular fcgiapp.h goes into /usr/local/include. Let's see if ruby_fcgi can find it!

ruby_fcgi

su ; gem install fcgi
Nothing to it, it just goes buzz-click. Sadly though the whole mess doesn't work and all I get are 500 errors at the time of this writing.

Apache configuration

This is where I begin to pull my hair out, nobody seems to give the straight scoop on this, or they assume this is second nature for you. It is certainly true that everyones apache config is likely to be unique. Anyway, here is how I did things:

My rails project is in /www/main/rails, and it turns out the only part you need and want to expose to your web server is /www/main/rails/public. To make this available to the world at large I could make a symbolic link from /www/main/app to /www/main/rails/public, but I use an apache Alias, which may or may not be a great idea.

In keeping with Apache 2.0 and the way Fedora does things, I create rails.conf in /etc/httpd/conf.d as follows:

# Set up mod_fcgid and ruby on rails

LoadModule fcgid_module     modules/mod_fcgid.so

IPCCommTimeout 40
IPCConnectTimeout 10
DefaultInitEnv RAILS_ENV production
SocketPath /tmp/fcgidsock

Alias /rails /www/main/rails/public

<Directory /www/main/rails/public>
#    AllowOverride FileInfo Options
    AllowOverride All
    AddHandler fcgid-script .fcgi
</Directory>

Also, some things need to be changed in DocumentRoot/rails/public/.htaccess . My final version follows. The lines I change have my initials (tjt), in particular I comment out the first two AddHandler lines (we just do that in the main config file for this directory). I have to specify a RewriteBase since I am using an Alias to get to rails/public, and I change the rewrite rule to use dispatch.fcgi instead of dispatch.cgi.

# General Apache options
#tjt#AddHandler fastcgi-script .fcgi
#tjt#AddHandler cgi-script .cgi
Options +FollowSymLinks +ExecCGI

# If you don't want Rails to look in certain directories,
# use the following rewrite rules so that Apache won't rewrite certain requests
# 
# Example:
#   RewriteCond %{REQUEST_URI} ^/notrails.*
#   RewriteRule .* - [L]

# Redirect all requests not available on the filesystem to Rails
# By default the cgi dispatcher is used which is very slow
# 
# For better performance replace the dispatcher with the fastcgi one
#
# Example:
#   RewriteRule ^(.*)$ dispatch.fcgi [QSA,L]
RewriteEngine On

# If your Rails application is accessed via an Alias directive,
# then you MUST also set the RewriteBase in this htaccess file.
#
# Example:
#   Alias /myrailsapp /path/to/myrailsapp/public
#   RewriteBase /myrailsapp

# tjt - add the following line or mod_rewrite
#       goes into a loop (since I use an Alias, see above)
RewriteBase /rails
RewriteRule ^$ index.html [QSA]
RewriteRule ^([^.]+)$ $1.html [QSA]
RewriteCond %{REQUEST_FILENAME} !-f
#tjt#RewriteRule ^(.*)$ dispatch.cgi [QSA,L]
RewriteRule ^(.*)$ dispatch.fcgi [QSA,L]

# In case Rails experiences terminal errors
# Instead of displaying this message you can supply a file here which will be rendered instead
# 
# Example:
#   ErrorDocument 500 /500.html

ErrorDocument 500 "Application error: Rails application failed to start properly"

Then I do service httpd restart to restart apache.

Trouble with ruby_fcgid

I use my browser to go to http://www.yadayada.com/rails and get:
Application error
Rails application failed to start properly
And my error_log file shows:
Dispatcher failed to catch: undefined method `is_cgi?' for FCGI:Class (NoMethodError)
/usr/lib/ruby/gems/1.8/gems/fcgi-0.8.6.1/./fcgi.rb:593:in `each_cgi'
/usr/lib/ruby/gems/1.8/gems/rails-1.1.3/lib/fcgi_handler.rb:53:in `process!'
/usr/lib/ruby/gems/1.8/gems/rails-1.1.3/lib/fcgi_handler.rb:23:in `process!'
/www/main/rails/public/dispatch.fcgi:24

A google search shows that a lot of folks have tripped over this, and it is something to do with ruby_fcgid not getting built properly on Fedora.
Here is a relevant link:

The advice is to do the following:

gem uninstall fcgi
download tarball from http://raa.ruby-lang.org/list.rhtml?name=fcgi
follow README ... I do as follows:
ruby install.rb config -- --with-fcgi-inc lude=/usr/local/include --with-fcgi-lib=/usr/local/lib
ruby install.rb setup
su ; ruby install.rb install
(puts stuff into /usr/lib/ruby/site_ruby/1.8 and /usr/lib/ruby/site_ruby/1.8/i386-linux
make sure dispatch.fcgi has
require 'fcgi'
and not require 'rubygems' require_gem 'fcgi'

I should note that the uninstalled "gem" was 0.8.6.1 and the downloaded tarball is 0.8.7.

Now I just get a 500 error no matter what I do.

One thing I should do is to make the log directory writeable by user apache. There are more secure ways to do this, but what I do is:

chmod a+w rails/public/log

The bottom line

It has been a tough fight with this all day, and it still doesn't work. It is smooth as silk with webrick on port 3000, but that won't do for an application that needs to be deployed to the real world on a server already running numerous other applications via apache on port 80. I've had it for the day, this may be one of these things that I never get enough time to push over the hill, nice though it might be.

A comment on speed and .htaccess files

While doing all this, I ran across an interesting note from Paul Querna:

Do not use .htaccess files. They are slow. They increase the risk of various security issues.

They are not required to get “a finer site control”.

If you have access to the httpd.conf, or an included file, you should ALWAYS use this over .htaccess files. Just put any directives that you would of put inside the .htaccess file, into a Directory block:

AddHandler fcgid-script .fcgi

This gives you more control that any .htacces file, since you can also do various regular expressions for matching paths. There are also some commands that only work from the main config files.


Feedback? Questions? Drop me a line!

Ruby on Rails notes / tom@mmto.org