What is this?

The following describes setting up "Ruby On Rails" utilizing lighttpd on a debian server. This should be useful to *nix users who wish to run rails on lighttpd.

Why?

lighttpd offers much higher static performance than apache, as well as comparable performance on dynamic pages. The largest benefit is the ability to run your applications on a server other than the webserver, by having your fastcgi processes listen on tcp and telling lighttpd to load balance among many listening fastcgi processes (on as many servers as you like). This not only allows for a much stricter security model (your application need not run on the webserver or as the webserver user), it gives you the ability to scale to many backend application processes while still maintaining only one externally facing webserver.

Who?

This document attempts to be detailed enough to start 'from scratch'; however, current rails users will probably find it more useful than first-time Railers. The author recommends checking in to #rubyonrails on the Freenode irc nettwork before diving in, for a first-time rails user/evaluator.

What else?

Conventions of this document are as follows:

		All commands to be run as root are in Cyan and are
		prepended with # 

		All commands to be run as a regular user are in Yellow and are
		prepended with ~$ 

		All newly created configuration files are in Bold and are
		delimited with 

		--- File <filename> starts 

		and end with 

		--- File <filename> EOF

		Do not include these delimiters in the configuration file
		
		Configuration instructions for lighttpd.conf are in <<<Bold Red>>>
		And are followed by an example directive that you may or not need to modify

The example that follows is built on Debian 3.1 (sarge):

(The instructions assume you are running as root for steps 1-5, excluding steps 3a-c)
1: Install ruby and gems (Easy Way)
 
# echo 'deb ftp://bougyman.com unstable main' >> /etc/apt/sources.list
# apt-get update && apt-get install ruby1.8-base libpgsql-ruby1.8 (or libmysql-ruby1.8)

1: The Hard Way
# apt-get install libtest-unit-ruby1.8 libtest-unit-ruby libyaml-ruby1.8 \
  libyaml-ruby libpgsql-ruby1.8 libpgsql-ruby ruby1.8 ruby ri1.8 ri \
  irb1.8 irb postgresql ruby1.8-dev libcfgi-ruby1.8 libfcgi0

1a: These packages are necessary prerequisites to ruby on rails with the postgresql database server.  For mysql support:
  # apt-get install libmysql-ruby1.8 libmysql-ruby mysql-server-4.1

1b: Link ruby to /usr/local/bin, for compatibility with rails' defaults
  # ln -s /usr/bin/ruby1.8 /usr/local/bin/ruby

2:  You should now have either postgresql or mysql server started, time to get gems:
  # cd /usr/local/src
  # wget http://rubyforge.org/frs/download.php/2412/rubygems-0.8.4.tgz

2a: Extract it:
  # tar zxvf rubygems-0.8.4.tgz
  -- Weee, watch the scroll

2b: Now install it:
  # cd rubygems-0.8.4
  # ruby setup.rb config
  # ruby setup.rb setup
  # ruby setup.rb install
  -- More scroll, check for errors, report them to bougy.man@gmail.com

3: Update gem list and get your rails installed
  # gem install rails 
  -- Information about updates, you will be prompted to install rails
  -- dependencies here, answer Y to the dependency questions

3a: Create a rails skeleton application for your user (DO THIS AS YOUR USER, NOT ROOT)
    Replace 'bougyhome' with any name you choose, noting that from this point on, $RAILS_ROOT
    will refer to /home/<username>/path/to/bougyhome, where 'bougyhome' is replaced with your app name
    # su - <username>
    ~$ rails bougyhome
    -- Lots of scroll, if this errors out, stop and seek help

3b: Modify your $RAILS_ROOT/public/dispatch.fcgi (add RAILS_ENV line), change this
    later to move to 'production'
    ~$ $EDITOR $RAILS_ROOT/public/dispatch.fcgi
    --- File $RAILS_ROOT/public/dispatch.fcgi starts
    #!/usr/local/bin/ruby
    ENV['RAILS_ENV']="development"
    require File.dirname(__FILE__) + "/../config/environment"
    require 'dispatcher'
    require 'fcgi'

    FCGI.each_cgi { |cgi| Dispatcher.dispatch(cgi) }
    --- File $RAILS_ROOT/public/dispatch.fcgi EOF

3c: Setup your environments, this example uses postgres, it's expected you are familiar
    with creating the necessary databases (createdb/psql/pgadmin3).  Modify as needed for
    mysql or sqlite
    ~$ $EDITOR $RAILS_ROOT/config/database.yaml
    --- File $RAILS_ROOT/config/database.yaml starts
development:
  adapter: postgresql
  database: bougyman.com
  host: localhost
  username: bougyman
  password: uh-uh

production:
  adapter: postgresql
  database: dev-bougyman.com
  host: localhost
  username: bougyman
  password: uh-uh

test:
  adapter: postgresql
  database: test-bougyman.com
  host: localhost
  username: bougyman
  password: uh-uh
    --- File $RAILS_ROOT/config/database.yaml EOF

3d: Go back to root
    ~$ exit
    -- should take you back to root (#)

4: Get lighttpd
  # cd /usr/local/src
  # wget http://www.lighttpd.net/download/lighttpd-1.3.14.tar.gz

4a: Extract it
  # tar zxvf lighttpd-1.3.14.tar.gz
  -- A bit of scroll

4a 1/2: You need to install any dependencies you'll need for lighttpd, such as openssl

4b: build a debian package
  # cd lighttpd-1.3.7
  # dpkg-buildpackage
  -- Lots of scroll, should end with:
  dpkg-deb: building package `lighttpd' in `../lighttpd_1.3.14-1_i386.deb'.
   dpkg-genchanges
   dpkg-genchanges: including full source code in upload
   dpkg-buildpackage: full upload; Debian-native package (full source is included)

4c: install lighttpd debian package
   # cd /usr/local/src
   # dpkg -i lighttpd_1.3.14-1_i386.deb

5: Set up services (this will install a supervisor to use)
   # apt-get install runit

5a: Create the service directories
   # mkdir /home/<username>/service
   # chown <username> /home/<username>/service
   # mkdir -p /etc/runit/homeservices/<username>/log/main
   # chown -R <username> /etc/runit/homeservices/<username>/log

5b: create the runscript to start your users services, using your favorite editor (i.e. vi /etc/runit/homeservices/<username>/run)
   # $EDITOR /etc/runit/homeservices/<username>/run
   --- File /etc/runit/homeservices/<username>/run Starts
   #!/bin/sh
   exec chpst -u <username> runsvdir /home/<username>/service
   --- /etc/runit/homeservices/<username>/run EOF

5c: Then create a log starter file as /etc/runit/homeservices/<username>log/run
   # $EDITOR /etc/runit/homeservices/<username>log/run
   --- File start: /etc/runit/homeservices/<username>/log/run
   #!/bin/sh
   exec chpst -u <username> svlogd -t ./main
   --- /etc/runit/homeservices/<username>/log/run EOF

5d: Make them executable
   # chmod 700 /etc/runit/homeservices/<username>/run
   # chmod 700 /etc/runit/homeservices/<username>/log/run

5e: start the service
   # ln -s /etc/runit/homeservices/<username> /var/service
   -- wait 5 seconds, then
   # runsvstat -l /var/service/<username>
   -- Should output status, similar to (but more like 2 seconds)
   /var/service/bougyman:
   run (pid 28349) 1265329 seconds
     log: run (pid 28348) 1265330 seconds

5f: Leave root at this time and log in as the <username> chosen above
    ~ is your users home directory from here on out.
    # su - <username>
    or
    # exit
    login: username
    password: *******
    ~$ export RAILS_ROOT=~/bougyhome
    -- replace ~/bougyhome with whatever you named your application

6: setup your local services directory
    ~$ mkdir -p ~/services/lighttpd/log
    ~$ cd services/lighttpd/log
    ~$ ln -s $RAILS_ROOT/log ./main
    ~$ cd ~/services/lighttpd

6a: Create the service file for lighttpd (~/services/lighttpd/run)
    ~$ $EDITOR ~/services/lighttpd/run
     --- File ~/services/lighttpd/run starts
     #!/bin/sh -e
     exec 2>&1\
     /usr/sbin/lighttpd -D -f ~/services/lighttpd/lighttpd.conf
     --- ~/services/lighttpd/run EOF

6b: Create the service logging file for lighttpd (~/services/lighttpd/run)
     --- File ~/services/lighttpd/run starts
     #!/bin/sh -e
     exec svlogd -t ./main
     --- ~/services/lighttpd/run EOF

6c: Create your lighttpd.conf (authors instructions in <<<Stuff>>>)
    ~$ cp /etc/lighttpd/lighttpd.conf ~/services/lighttpd/
    ~$ $EDITOR ~/services/lighttpd/lighttpd.conf
    --- File ~/services/lighttpd/lighttpd.conf starts

# lighttpd configuration file
# 
# use a it as base for lighttpd 1.0.0 and above
#
# $Id: lighttpd.conf,v 1.7 2004/11/03 22:26:05 weigon Exp $

############ 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 needs to be enabled>>>
                               "mod_rewrite",
#                               "mod_redirect", 
                    "mod_access",
#               "mod_auth", 
#                               "mod_status", 
#<<<mod_fastcgi needs to be enabled>>>
                "mod_fastcgi",
#               "mod_simple_vhost",
#               "mod_evhost",
#               "mod_cgi",
#               "mod_compress",
#                               "mod_ssi",
#                               "mod_usertrack",
#               "mod_rrdtool",
                "mod_accesslog" )

## a static document-root, for virtual-hosting take look at the 
## server.virtual-* options
#<<<Set to full path to your $RAILS_ROOT/public>>>
server.document-root             = "/home/bougyman/www/bougyhome/public"

## where to send error-messages to
#<<<Set to full path to your $RAILS_ROOT/log/lighttpd.error.log>>>
server.errorlog            = "/home/bougyman/www/bougyhome/log/lighttpd.error.log"

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

#### accesslog module
#<<<Full path to $RAILS_ROOT/log/access.log>>>
accesslog.filename          = "/home/bougyman/www/bougyhome/log/access.log"

## deny access the file-extensions
#
# ~    is for backupfiles from vi, emacs, joe, ...
# .inc is often used for code includes which should in general not be part
#      of the document-root
url.access-deny             = ( "~", ".inc" )



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

## bind to port (default: 80)
#<<<Must set this to a high port (>1024) so your regular user can open the socket
server.port                = 8888

## bind to localhost (default: all interfaces)
#<<<Set to 127.0.0.1 for now, uncomment to listen on all interfaces later>>>
server.bind                = "127.0.0.1"

--- SNIP ---

#<<<Comment out all of these, they only apply to root>>>
### only root can use these options
#
# chroot() to directory (default: no chroot() )
#server.chroot            = "/"

## change uid to <uid> (default: don't care)
#server.username            = "wwwuser"

## change uid to <uid> (default: don't care)
#server.groupname           = "wwwgroup"

#### compress module
#compress.cache-dir          = "/var/tmp/lighttpd/cache/compress/"
#compress.filetype           = ("text/plain", "text/html")

#### fastcgi module
## read fastcgi.txt for more info
#<<<Here is the dirty work, feel free to eliminate the .php entry if you don't need it>>>
#<<<for the php4 entry to work you need to 'apt-get install php4-cgi'>>>
#<<<bin-path should be the full path to $RAILS_ROOT/public/dispatch.fcgi>>>
#<<<you can spawn multiple fastcgi processes, 5 is a good starting point>>>
fastcgi.server = ( 
		".fcgi" =>
                    ( "localhost" =>
                        (
                            "socket" => "/tmp/rails.socket",
                            "bin-path" => "/home/bougyman/www/bougyhome/public/dispatch.fcgi"
                        )
                    ),
                    ( "localhost" =>
                        (
                            "socket" => "/tmp/rails1.socket",
                            "bin-path" => "/home/bougyman/www/bougyhome/public/dispatch.fcgi"
                        )
                    ),
                    ( "localhost" =>
                        (
                            "socket" => "/tmp/rails2.socket",
                            "bin-path" => "/home/bougyman/www/bougyhome/public/dispatch.fcgi"
                        )
                    ),
                    ( "localhost" =>
                        (
                            "socket" => "/tmp/rails3.socket",
                            "bin-path" => "/home/bougyman/www/bougyhome/public/dispatch.fcgi"
                        )
                    ),
                    ( "localhost" =>
                        (
                            "socket" => "/tmp/rails4.socket",
                            "bin-path" => "/home/bougyman/www/bougyhome/public/dispatch.fcgi"
                        )
                    ),
		".php" =>
                    ( "localhost" =>
                        (
                            "socket" => "/tmp/php-fcgi.socket",
                            "bin-path" => "/usr/bin/php4-cgi"
                        )
                    )
                 )

--- SNIP ---

#### url handling modules (rewrite, redirect, access)
# url.rewrite                 = ( "^/$"             => "/server-status" )
# url.redirect                = ( "^/wishlist/(.+)" => "http://www.123.org/$1" )
#<<<Url rewriting for pretty urls>>>
#<<<You'll need to set the first one to your default controller and action, replacing>>>
#<<<controller=home&action=index2 with your chosen controller and action>>>
url.rewrite = ( 
			"^/$" =>  "/dispatch.fcgi?controller=home&action=index2",
			"^/([\-_a-zA-Z0-9]+)/([\-_a-zA-Z0-9]+)/([\-_a-zA-Z0-9%]+)\??([\-_a-zA-Z0-9=&%]*)$"  => 
				"/dispatch.fcgi?controller=$1&action=$2&id=$3&$4",
			"^/([\-_a-zA-Z0-9]+)/([\-_a-zA-Z0-9]+)/?\??([\-_a-zA-Z0-9=&%]*)$"  => 
				"/dispatch.fcgi?controller=$1&action=$2&$3",
			"^/([\-_a-zA-Z0-9]+)/?\??([\-_a-zA-Z0-9=&%]*)$"  =>  
				"/dispatch.fcgi?controller=$1&action=index&$2"
)

--- SNIP ---

    --- ~/services/lighttpd/lighttpd.conf EOF

6d: Make your lighttpd server startup files executable
    ~$ chmod 700 ~/services/lighttpd/run ~/services/lighttpd/log/run

6e: test it out
    ~$ cd ~/services/lighttpd
    ~$ ./run
    -- should show the server startup, stop here on errors and seek help, else ctrl-c and move to next step

6f: Start the service permanently (supervised)
    ~$ ln -s ~/services/lighttpd ~/service
    -- wait 5 seconds, then
    ~$ runsvstat -l ~/service/lighttpd
    -- should output
    /home/bougyman/service/lighttpd: run (pid 12630) 4037 seconds
      log: run (pid 12629) 4038 seconds

7: Start creating your rails application! See www.rubyonrails.com for detailed instructions

NOTES

Debian will start lighttpd on port 80 with the configuration in /etc/lighttpd/lighttpd.conf
when you install. You probably want to

	# /etc/init.d/lighttpd stop 
		then 
	# update-rc.d lighttpd remove

so you can use iptables to redirect port 80 to your users port (8888) in the example above.

example iptables rules:

	# iptables -t nat -I PREROUTING -p tcp --dport 80 -j DNAT --to 127.0.0.1:8888
	# iptables -t nat -I POSTROUTING -p tcp --sport 8888 -j SNAT --to 127.0.0.1:80

You'll replace localhost/127.0.0.1 with the ip lighttpd will bind to in production. For now we are only working with 127.0.0.1.

TJ Vanderpoel bougy.man@gmail.com
Last update: Jan 9, 2004