Ruby on Rails via Lighttpd

I spent a good part of a week trying to get rails to work under Apache and then gave up. Now I am giving it a try with lighttpd. To steal some words from Duncan Davidson (see links below):

Lighty is fast. Clean. Easy to compile. Easy to configure.
If you have a choice about it, the choice is to use lighty for rails.

Here are some links:

Setting up basic rails

The first thing you will need is the gems system:

tar xzvf rubygems-0.9.0.tgz
cd rubygems-0.9.0
su
ruby setup.rb

Then you need rails itself, and I jumped right in and set up a demo application and went through the instant gratification example in the "agile" book.

su
gem install rails --include-dependencies
mkdir /u2/rails
cd /u2/rails
rails demo

Then as a precursor to getting this to work with lighttpd, I go to my documentroot and do:

cd DocumentRoot
ln -s /u2/rails/demo/public rails

FastCGI

You need this before you can build ruby_fcgi. It is unclear and/or foggy to me why you need to build this when you use lighttpd's own built in fastcgi module, but you do! (There must be a high level of binary compatibility here). Get the fastcgi development kit and do this:
get fcgi-2.4.0.tar.gz
tar xzvf fcgi-2.4.0.tar.gz
cd fcgi-2.4.0
./configure
make
make install
Note that I do not su to do this. I make sure that /usr/local is owned by me so the make install can put things there when run as an ordinary user. In particular it puts fcgiapp.h into /usr/local/include.
The fcgi library is also required to build ruby_fcgi.

Ruby fcgi

Perhaps you could get this as a gem. I choose to get it as source and build it myself, since I have heard that prebuilt ruby_fcgi has had troubles on fedora systems:
Get ruby-fcgi-0.8.6.tar.gz
tar xzvf ruby-fcgi-0.8.6.tar.gz
cd ruby-fcgi-0.8.6
ruby install.rb config
ruby install.rb setup
ruby install.rb install

Several things can go wrong here.

If you have not already installed the fastcgi development kit, it will get into trouble looking for the fastcgi header file: fcgiapp.h.

The install phase will try to put things into /usr/lib/ruby/site_ruby/1.8 so you will probably need to su or change permissions for that to work.

The linux dyamic load path may not include /usr/local/lib. Test this by running:

irb
require "fcgi.so"
In my case it failed trying to get libfcgi.so.0. The fix is to edit the file /etc/ld.so.conf, or actually in my case I created a new file /etc/ld.so.conf.d with the single line:
/usr/local/lib
And all was well after doing this and running ldconfig.

Pain and anguish

I had a happy rails setup that worked with webrick, doing the instant gratification example from the "agile" book in /u2/rails/demo, so I set up my documentroot to be a symbolic link to /u2/rails/demo/public, and used the lighttpd configuration from the back of the "agile" book.

I get the index.html page just fine, but any other request just gives me:

Application error

Change this error message for exceptions thrown outside of an action (like in Dispatcher setups or broken Ruby code) in public/500.html

Note that the above is the contents of /u2/rails/demo/public/500.html.

After lots of frustrating experimentation with my lighttpd.conf file setup, I decide I had better check if I can really lauch ruby fastcgi scripts.

I replace /u2/rails/demo/public/dispatch.fcgi with mycgi.fcgi which looks like so:

#!/usr/bin/ruby

require 'fcgi'

FCGI.each { |req|
	out = req.out
	out.print "Content-type: text/plain\r\n"
	out.print "\r\n"
	out.print "Hello, the time is: ", Time.now.to_s
	req.finish
}
This works just fine, telling me that my setup of lighttpd and ruby_fcgi is entirely healthy. Something however is wrong with my rails setup.

Success at last

A quick look at /u2/rails/demo/log/development.log has a dandy traceback that tells the story. Rails is unable to access a session file:
Permission denied - /u2/rails/demo/public/../config/../tmp/sessions//ruby_sess.456505837f37240a
What I have had to do is the following:
cd /u2/rails/demo
chmod a+w log
cd /u2/rails/demo/log
chmod a+w *
cd /u2/rails/demo/tmp
chmod a+w *
Hurrah!! Now things are cool. I sure wish this was in the manuals.

My first working lighttpd.conf file

# lighttpd configuration file
# Just to run rails
#
############ Options you really have to take care of ####################

## modules to load
# at least mod_access and mod_accesslog should be loaded
# all other module should only be loaded if really neccesary
# - saves some time
# - saves memory
server.modules              = (
                               "mod_rewrite",
#                               "mod_redirect",
#                               "mod_alias",
                               "mod_access",
#                               "mod_cml",
#                               "mod_trigger_b4_dl",
                               "mod_auth",
#                               "mod_status",
#                               "mod_setenv",
                               "mod_fastcgi",
#                               "mod_proxy",
#                               "mod_simple_vhost",
#                               "mod_evhost",
#                               "mod_userdir",
#                               "mod_cgi",
#                               "mod_compress",
#                               "mod_ssi",
#                               "mod_usertrack",
#                               "mod_expire",
#                               "mod_secdownload",
#                               "mod_rrdtool",
                               "mod_accesslog" )

## a static document-root, for virtual-hosting take look at the
## server.virtual-* options
server.document-root        = "/www/rails/"

## where to send error-messages to
server.errorlog             = "/var/log/lighttpd/error_log"

# files to check for if .../ is requested
index-file.names            = ( "index.php", "index.html",
                                "index.htm", "default.htm" )

## set the event-handler (read the performance section in the manual)
# server.event-handler = "freebsd-kqueue" # needed on OS X

# mimetype mapping
mimetype.assign             = (
  ".rpm"          =>      "application/x-rpm",
  ".pdf"          =>      "application/pdf",
  ".sig"          =>      "application/pgp-signature",
  ".spl"          =>      "application/futuresplash",
  ".class"        =>      "application/octet-stream",
  ".ps"           =>      "application/postscript",
  ".torrent"      =>      "application/x-bittorrent",
  ".dvi"          =>      "application/x-dvi",
  ".gz"           =>      "application/x-gzip",
  ".pac"          =>      "application/x-ns-proxy-autoconfig",
  ".swf"          =>      "application/x-shockwave-flash",
  ".tar.gz"       =>      "application/x-tgz",
  ".tgz"          =>      "application/x-tgz",
  ".tar"          =>      "application/x-tar",
  ".zip"          =>      "application/zip",
  ".mp3"          =>      "audio/mpeg",
  ".m3u"          =>      "audio/x-mpegurl",
  ".wma"          =>      "audio/x-ms-wma",
  ".wax"          =>      "audio/x-ms-wax",
  ".ogg"          =>      "application/ogg",
  ".wav"          =>      "audio/x-wav",
  ".gif"          =>      "image/gif",
  ".jpg"          =>      "image/jpeg",
  ".jpeg"         =>      "image/jpeg",
  ".png"          =>      "image/png",
  ".xbm"          =>      "image/x-xbitmap",
  ".xpm"          =>      "image/x-xpixmap",
  ".xwd"          =>      "image/x-xwindowdump",
  ".css"          =>      "text/css",
  ".html"         =>      "text/html",
  ".htm"          =>      "text/html",
  ".js"           =>      "text/javascript",
  ".asc"          =>      "text/plain",
  ".c"            =>      "text/plain",
  ".cpp"          =>      "text/plain",
  ".log"          =>      "text/plain",
  ".conf"         =>      "text/plain",
  ".text"         =>      "text/plain",
  ".txt"          =>      "text/plain",
  ".dtd"          =>      "text/xml",
  ".xml"          =>      "text/xml",
  ".mpeg"         =>      "video/mpeg",
  ".mpg"          =>      "video/mpeg",
  ".mov"          =>      "video/quicktime",
  ".qt"           =>      "video/quicktime",
  ".avi"          =>      "video/x-msvideo",
  ".asf"          =>      "video/x-ms-asf",
  ".asx"          =>      "video/x-ms-asf",
  ".wmv"          =>      "video/x-ms-wmv",
  ".bz2"          =>      "application/x-bzip",
  ".tbz"          =>      "application/x-bzip-compressed-tar",
  ".tar.bz2"      =>      "application/x-bzip-compressed-tar"
 )

# Use the "Content-Type" extended attribute to obtain mime type if possible
#mimetype.use-xattr        = "enable"


## send a different Server: header
## be nice and keep it at lighttpd
# server.tag                 = "lighttpd"

#### accesslog module
accesslog.filename          = "/var/log/lighttpd/access_log"

######### Options that are good to be but not neccesary to be changed #######

## bind to port (default: 80)
#server.port                = 81

## bind to localhost (default: all interfaces)
#server.bind                = "grisu.home.kneschke.de"

## to help the rc.scripts
server.pid-file             = "/var/run/lighttpd.pid"


## enable debugging
#debug.log-request-header   = "enable"
#debug.log-response-header  = "enable"
#debug.log-request-handling = "enable"
#debug.log-file-not-found   = "enable"

### only root can use these options
#
# chroot() to directory (default: no chroot() )
#server.chroot              = "/"

## change uid to  (default: don't care)
#server.username             = "lighttpd"
server.username             = "www"

## change uid to  (default: don't care)
#server.groupname            = "lighttpd"
server.groupname            = "www"

# Setup for rails
url.rewrite                = ( "^/$" => "index.html", "^([^.]+)$" => "$1.html" )
# Any file that exists with extension .fcgi would do here.
server.error-handler-404 = "/mycgi.fcgi"

#### fastcgi module
## read fastcgi.txt for more info
fastcgi.server             = ( ".fcgi" =>
                               ((
                                   "min-procs" => 4,
                                   "max-procs" => 10,
                                   "socket" => "/tmp/rails.fastcgi.socket",
                                   "bin-path" => "/u2/rails/demo/public/dispatch.fcgi",
##                                   "bin-path" => "/u2/rails/demo/public/mycgi.fcgi",
				   "bin-environment" => ( "RAILS_ENV" => "development" )
                                ))
                            )

# END

Feedback? Questions? Drop me a line!

Ruby on Rails notes / tom@mmto.org