The Perfect Rails/debian/lighttpd Stack...

Our setup starts with a minimal net-install image of debian-3.1 sarge. There is just the base system. Although sometimes apache or apache2 might be installed already. We are going to blow away apache and only use lighttpd to save on precious resources while getting a faster rails server at the same time. Ask your hosting provider to install the webmin admin interface for your server if you want any easy way to check your email online and create users and email addresses and a lot of other admin tasks with an easy gui. If you are a hardcore command line junky like me you can forgo this part and just use the command line like it was in the beginning ;-)


ssh in to your new account's IP with your root user and password.
create a user for your self with:
# adduser


then add yourself to the sudoers list. only we don't have the sudo program yet even! so get it:
# apt-get install sudo

now edit /etc/sudoers
# pico /etc/sudoers

add this line at the end:
yourusername ALL=(ALL) ALL

Its always better security wise to ssh into your machine as your own user instead of root. So we will dis-allow root logins over ssh. And then you will use su or sudo to become root as needed.

# pico edit /etc/ssh/sshd_config

edit the line:
PermitRootLogin yes
to:
PermitRootLogin no

This step is entirely optional but I highly recommend it!

Its a good idea to run sshd on a higher port. This will keep you safe from all the automated ssh break-in bots and skript kiddies. Most of these automated breaking attempt over ssh are done by scripts that do a port scan on your box and try to log in with ssh if they find it running. Which they will if it is on the standard port 22. Most of these attacks will not scan ports higher than 1024 in their search for sshd. So choose a port you can live with and edit the /etc/ssh/sshd_config file to change the port that sshd runs on. set the line:
Port 22
to:
Port >1024 where i mean any unused port above 1024. and then save the file and quit the editor. Now make sshd reload its config file to pick up the new settings:

# /etc/init.d/ssh reload



Now we will use our friends apt-get and apt-cache search to get our gcc c compiler toolchain so we can compile source code when needed and then move on to installing many packages:

The first thing I like to do is make a shortcut for the shell.
I like to make one for apt-cache search because I get tired of typing that whole thing out when searching for the right packages to install. So we will make sapt do that.

# pico /usr/local/bin/sapt

and add this to it and save.
#!/usr/local/bin/sapt
apt-cache search $*

Lets get rid of apache2 if it was installed by the hosting provider:

# dpkg --purge apache2


Lets install ruby and friends:


# apt-get install ruby1.8-dev ruby1.8 ri1.8 rdoc1.8 irb1.8 libreadline-ruby1.8 libruby1.8

Now we need to make some symlinks:

# ln -s /usr/bin/ruby1.8 /usr/local/bin/ruby
# ln -s /usr/bin/ri1.8 /usr/local/bin/ri
# ln -s /usr/bin/rdoc1.8 /usr/local/bin/rdoc
# ln -s /usr/bin/irb1.8 /usr/local/bin/irb

# apt-get install gcc-3.4-doc gcc-3.4 g++-3.4

We need a few sylimks for these to work for compiling lighttpd which is written in C++

# ln -s /usr/bin/g++-3.4 /usr/bin/g++
# ln -s /usr/bin/gcc-3.4 /usr/bin/gcc

Lets install postfix here really quick. We will configure it later but if we don't install it before we install mysql, apt will pull in exim for some reason when you try to install mysql. We don't want this so lets pre empt it by installing postfix.

Internet Site
yourusername
Your default.com domain
example.com, localhost.com, , localhost
No

install mysql and zlib1-devel and top zlib1g-dev
# apt-get install mysql-server-4.1 mysql-common-4.1 mysql-client-4.1 libmysqlclient14-dev libmysqlclient14 mytop zlib1g-dev

Answer:
OK

Now lets set the root password for our mysql database:

# mysqladmin -u root password yourrootsqlpassword

$ libmysql-ruby1.8

Now test it:
root@example:/etc/ssh# irb
irb(main):001:0> require 'mysql'
=> true
irb(main):002:0> exit

Now we will install rubygems so we can get rails and all kinds of other ruby treasures!

# wget http://rubyforge.org/frs/download.php/3700/rubygems-0.8.10.tgz
# tar xvzf rubygems*
# cd rubygems*
# ruby setup.rb


Now lets get rid of the rubygems source dir to save space for later:

# cd ..
# rm -rf rubygems-0.8.10 rubygems-0.8.10.tgz

Now lets install the latest version of rails:

# gem install rails --include-dependencies

You should see:
Attempting local installation of 'rails'
Local gem file not found: rails*.gem
Attempting remote installation of 'rails'
Updating Gem source index for: http://gems.rubyforge.org
Successfully installed rails-0.14.3
Successfully installed rake-0.6.2
Successfully installed activesupport-1.2.3
Successfully installed activerecord-1.13.0
Successfully installed actionpack-1.11.0
Successfully installed actionmailer-1.1.3
Successfully installed actionwebservice-0.9.3
Installing RDoc documentation for rake-0.6.2...
Installing RDoc documentation for activesupport-1.2.3...
Installing RDoc documentation for activerecord-1.13.0...

lib/active_record.rb:73:64: Skipping require of dynamic string: "active_record/connection_adapters/#{adapter}_adapter"
Installing RDoc documentation for actionpack-1.11.0...

lib/action_controller/assertions.rb:4:69: Skipping require of dynamic string: "#{File.dirname(__FILE__)}/vendor/html-scanner/html/document"
Installing RDoc documentation for actionmailer-1.1.3...
Installing RDoc documentation for actionwebservice-0.9.3...

Don't worry about those couple of errors. That just happens sometimes when building the rdoc documentation. If you look close you will see that it say Successfully installed foobar... a bunch of times.

Alright, we've already made it pretty far! So far we have ruby and friends with mysql and postfix installed. Lets move on to lighttpd and the fastcgi stuff.

Get the fcgi-dev headers so we can compile lighttpd:

# apt-get install libfcgi-dev

Now lets install the ruby-fcgi bindings from source so we no we have the good ones:

wget http://sugi.nemui.org/pub/ruby/fcgi/ruby-fcgi-0.8.6.tar.gz
tar xzvf ruby-fcgi*
cd ruby-fcgi-0.8.6
ruby install.rb config
ruby install.rb setup
sudo ruby install.rb install

Now lets make sure it works:

root@example:~/ruby-fcgi-0.8.6# irb
irb(main):001:0> require 'fcgi'
=> true
irb(main):002:0>


Cleanup the ruby-fcgi source code:
# cd ..
# rm -rf ruby-fcgi-0.8.6 ruby-fcgi-0.8.6.tar.gz


Alright cool, on to lighty! First we need the pcre(perl compatible regular expression) Lighty needs these for mod_rewrite and config file processing:

# wget ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/pcre-5.0.tar.gz
# tar xzvf pcre-5.0.tar.gz
# cd pcre-*
# ./configure
# make
# make test
# make install

Cleanup:
root@example:~/pcre-5.0# cd ..
root@example:~# rm -rf pcre-5.0*

# wget http://www.lighttpd.net/download/lighttpd-1.4.7.tar.gz
# tar xzvf lighttpd*
# cd light*
# ./configure
You should now see this after it gets done scrolling by:
Plugins:

enabled:
mod_access
mod_accesslog
mod_alias
mod_auth
mod_cgi
mod_compress
mod_dirlisting
mod_evhost
mod_expire
mod_fastcgi
mod_indexfiles
mod_proxy
mod_redirect
mod_rewrite
mod_rrdtool
mod_scgi
mod_secdownload
mod_setenv
mod_simple_vhost
mod_ssi
mod_staticfile
mod_status
mod_trigger_b4_dl
mod_userdir
mod_usertrack
mod_webdav
disabled:
mod_cml
mod_mysql_vhost

Features:

enabled:
auth-crypt
compress-deflate
compress-gzip
large-files
network-ipv6
regex-conditionals
disabled:
auth-ldap
compress-bzip2
network-openssl
stat-cache-fam
storage-gdbm
storage-memcache
webdav-properties
If you get that then you are all set. You want to make sure that you get the lines in the Enabled section: mod_fastcgi mod_rewrite mod_scgi mod_simple_vhost
Now on with lighttpd:

# make
# make install

Now test to make sure you have lighty and its in your $PATH:

root@example:~/lighttpd-1.4.7# lighttpd
2005-11-13 04:20:56: (server.c.364) No configuration available. Try using -f option.

Thats the error we want to see! That means that lighttpd is working and installed correctly but just can't find a config file because you didn't specify one. Lets clean up after ourselves:

# cd ..
# rm -rf light*

So that brings us into a pretty good stack. But we still need subversion and postfix config. Also why don't we just install Imagemagick and Rmagick while we're at it huh? This thing gives everyone trouble so lets do it and be done with it because its really cool and eventually you will want to use it in one of your rails apps:

# apt-get install imagemagick librmagick-ruby1.8 librmagick-ruby-doc libfreetype6-dev xml-core

say yes to the install question you don't need any of the suggested packages. Remember, we are shooting for lightweight! So now lets test that rmagick works:

root@smarkets:~/lighttpd-1.4.7# irb
irb(main):001:0> require 'RMagick'
=> true
irb(main):002:0> exit

Sweet! We have a great rails stack now from ruby->rails->mysql and bindings->lighttpd->fcgi and the bindings->RMagick too! So lets get subversion now and then we will configure postfix:

# apt-get install subversion subversion-tools libsvn0-dev libsvn0
answer yes


OK lets get to the postfix set up. We will install postfix with a pop3 servers and imap plus sasl auth and tls for smtp. This will give us a secure lightweight mail server with authenticated pop and smtp:

Postfix/POP3/IMAP

In order to install Postfix with SMTP-AUTH and TLS as well as a POP3 server that also does POP3s (port 995) and an IMAP server that is also capable of IMAPs (port 993) do the following steps:

apt-get install postfix postfix-tls libsasl2 sasl2-bin libsasl2-modules ipopd-ssl uw-imapd-ssl (1 line!)

<- pop3 and pop3s
<- No
<- Internet Site
<- NONE
<- server1.example.com
<- server1.example.com, localhost.example.com, localhost
<- No

postconf -e 'smtpd_sasl_local_domain ='
postconf -e 'smtpd_sasl_auth_enable = yes'
postconf -e 'smtpd_sasl_security_options = noanonymous'
postconf -e 'broken_sasl_auth_clients = yes'
postconf -e 'smtpd_recipient_restrictions = permit_sasl_authenticated,permit_mynetworks,reject_unauth_destination'
postconf -e 'inet_interfaces = all'
echo 'pwcheck_method: saslauthd' >> /etc/postfix/sasl/smtpd.conf
echo 'mech_list: plain login' >> /etc/postfix/sasl/smtpd.conf

mkdir /etc/postfix/ssl
cd /etc/postfix/ssl/
openssl genrsa -des3 -rand /etc/hosts -out smtpd.key 1024
chmod 600 smtpd.key
openssl req -new -key smtpd.key -out smtpd.csr
openssl x509 -req -days 3650 -in smtpd.csr -signkey smtpd.key -out smtpd.crt
openssl rsa -in smtpd.key -out smtpd.key.unencrypted
mv -f smtpd.key.unencrypted smtpd.key
openssl req -new -x509 -extensions v3_ca -keyout cakey.pem -out cacert.pem -days 3650

postconf -e 'smtpd_tls_auth_only = no'
postconf -e 'smtp_use_tls = yes'
postconf -e 'smtpd_use_tls = yes'
postconf -e 'smtp_tls_note_starttls_offer = yes'
postconf -e 'smtpd_tls_key_file = /etc/postfix/ssl/smtpd.key'
postconf -e 'smtpd_tls_cert_file = /etc/postfix/ssl/smtpd.crt'
postconf -e 'smtpd_tls_CAfile = /etc/postfix/ssl/cacert.pem'
postconf -e 'smtpd_tls_loglevel = 1'
postconf -e 'smtpd_tls_received_header = yes'
postconf -e 'smtpd_tls_session_cache_timeout = 3600s'
postconf -e 'tls_random_source = dev:/dev/urandom'

The file /etc/postfix/main.cf should now look like this:
________________________________________________________
# See /usr/share/postfix/main.cf.dist for a commented, more complete version

smtpd_banner = $myhostname ESMTP $mail_name (Debian/GNU)
biff = no

# appending .domain is the MUA's job.
append_dot_mydomain = no

# Uncomment the next line to generate "delayed mail" warnings
#delay_warning_time = 4h

myhostname = server1.example.com
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases
myorigin = /etc/mailname
mydestination = server1.example.com, localhost.example.com, localhost
relayhost =
mynetworks = 127.0.0.0/8
mailbox_command = procmail -a "$EXTENSION"
mailbox_size_limit = 0
recipient_delimiter = +
inet_interfaces = all
smtpd_sasl_local_domain =
smtpd_sasl_auth_enable = yes
smtpd_sasl_security_options = noanonymous
broken_sasl_auth_clients = yes
smtpd_recipient_restrictions = permit_sasl_authenticated,permit_mynetworks,reject_unauth_destination
smtpd_tls_auth_only = no
smtp_use_tls = yes
smtpd_use_tls = yes
smtp_tls_note_starttls_offer = yes
smtpd_tls_key_file = /etc/postfix/ssl/smtpd.key
smtpd_tls_cert_file = /etc/postfix/ssl/smtpd.crt
smtpd_tls_CAfile = /etc/postfix/ssl/cacert.pem
smtpd_tls_loglevel = 1
smtpd_tls_received_header = yes
smtpd_tls_session_cache_timeout = 3600s
tls_random_source = dev:/dev/urandom
-------------------------------------------------------
Now:

# /etc/init.d/postfix restart

Authentication will be done by saslauthd. We have to change a few things to make it work properly. Because Postfix runs chrooted in /var/spool/postfix we have to do the following:

# mkdir -p /var/spool/postfix/var/run/saslauthd
# rm -fr /var/run/saslauthd

Now we have to edit /etc/default/saslauthd in order to activate saslauthd. Remove # in front of START=yes and add the line PARAMS="-m /var/spool/postfix/var/run/saslauthd":
--------------------------------------------------------
# This needs to be uncommented before saslauthd will be run automatically
START=yes

PARAMS="-m /var/spool/postfix/var/run/saslauthd"

# You must specify the authentication mechanisms you wish to use.
# This defaults to "pam" for PAM support, but may also include
# "shadow" or "sasldb", like this:
# MECHANISMS="pam shadow"

MECHANISMS="pam"
--------------------------------------------------------
Finally we have to edit /etc/init.d/saslauthd. Change the line

dir=`dpkg-statoverride --list $PWDIR`
to

#dir=`dpkg-statoverride --list $PWDIR`
Then change the variables PWDIR and PIDFILE and add the variable dir at the beginning of the file:
--------------------------------------------------------

PWDIR="/var/spool/postfix/var/run/${NAME}"
PIDFILE="${PWDIR}/saslauthd.pid"
dir="root sasl 755 ${PWDIR}"
/etc/init.d/saslauthd should now look like this:
--------------------------------------------------------
#!/bin/sh -e

NAME=saslauthd
DAEMON="/usr/sbin/${NAME}"
DESC="SASL Authentication Daemon"
DEFAULTS=/etc/default/saslauthd
PWDIR="/var/spool/postfix/var/run/${NAME}"
PIDFILE="${PWDIR}/saslauthd.pid"
dir="root sasl 755 ${PWDIR}"

createdir() {
# $1 = user
# $2 = group
# $3 = permissions (octal)
# $4 = path to directory
[ -d "$4" ] || mkdir -p "$4"
chown -c -h "$1:$2" "$4"
chmod -c "$3" "$4"
}

test -f "${DAEMON}" || exit 0

# Source defaults file; edit that file to configure this script.
if [ -e "${DEFAULTS}" ]; then
. "${DEFAULTS}"
fi

# If we're not to start the daemon, simply exit
if [ "${START}" != "yes" ]; then
exit 0
fi

# If we have no mechanisms defined
if [ "x${MECHANISMS}" = "x" ]; then
echo "You need to configure ${DEFAULTS} with mechanisms to be used"
exit 0
fi

# Add our mechanimsms with the necessary flag
PARAMS="${PARAMS} -a ${MECHANISMS}"

START="--start --quiet --pidfile ${PIDFILE} --startas ${DAEMON} --name ${NAME} -- ${PARAMS}"

# Consider our options
case "${1}" in
start)
echo -n "Starting ${DESC}: "
#dir=`dpkg-statoverride --list $PWDIR`
test -z "$dir" || createdir $dir
if start-stop-daemon ${START} >/dev/null 2>&1 ; then
echo "${NAME}."
else
if start-stop-daemon --test ${START} >/dev/null 2>&1; then
echo "(failed)."
exit 1
else
echo "${DAEMON} already running."
exit 0
fi
fi
;;
stop)
echo -n "Stopping ${DESC}: "
if start-stop-daemon --stop --quiet --pidfile "${PIDFILE}" \
--startas ${DAEMON} --retry 10 --name ${NAME} \
>/dev/null 2>&1 ; then
echo "${NAME}."
else
if start-stop-daemon --test ${START} >/dev/null 2>&1; then
echo "(not running)."
exit 0
else
echo "(failed)."
exit 1
fi
fi
;;
restart|force-reload)
$0 stop
exec $0 start
;;
*)
echo "Usage: /etc/init.d/${NAME} {start|stop|restart|force-reload}" >&2
exit 1
;;
esac

exit 0
-----------------------------------------------------------------
Now start saslauthd:

/etc/init.d/saslauthd start

To see if SMTP-AUTH and TLS work properly now run the following command:

telnet localhost 25

After you have established the connection to your postfix mail server type

ehlo localhost

If you see the lines

250-STARTTLS

and

250-AUTH

everything is fine. Type quit to get out of telnet.

Now we need to install qpopper. A pop3 server. when you install this it will tell you its removing some packages, specifically related to ipopd

# apt-get install qpopper
answer yes


Now you should be in great shape for rails development. Lets generate a skeleton rails app and fire it up with lighttpd to make sure everything is working fine. Lets switch to our user now as we don't need root for rails dev:

# su yourusername
# cd ~
# rails test

You need to add /usr/local/sbin to your path to run lighttpd:

Edit .bashrc

$ pico ~/.bashrc

Add this line at the end:
export PATH=/usr/local/sbin:$PATH

Now save and quit. OK lets try out our rails test project!

# cd test
# ruby script/server

Since we are running rails 1.0rc release or later, the script/server command will generate a lighttpd.conf file in RAILS_ROOT/config/lighttpd.conf and start lighttpd with it. If rails can't find lighttpd it will boot webrick instead. If it boots webrick, make sure lighttpd is in your $PATH and look back over the instructions to make sure you have followed them correctly. If it started fine then take a look at:

http://YOURIPADDRESS:3000

You should see the Congratulations, you've put Ruby on Rails! Page and we are done with the Perfect Debian Sarge Rails Stack Server! Enjoy your new sandboxed environment that no-one else can mess up like on a shared host.

Actually I lied a little bit. We need to install the webmin module for postfix and a few other niceties. This is only if you decided to have webmin installed. Our lightweight powerhouse rails install wants to be as light as possible sso if you don't need webmin leave it off.

$ sudo apt-get install webmin-postfix webmin-mysql webmin-mailboxes webmin-logrotate webmin-firewall webmin-status

That should give you a great amount of flexibility out of your webmin control panel. You can reach your control panel with the following address:
http://YOURIPADDRESS:10000

Log in with the root username/password and go to town tweaking your settings as much as you want!

Now lets see how much space we took up for our ultimate lightweight rails server system:
become root again:
# sudo -s

Then run these commands:
# cd /
# du -h

Look at the last number after all the text scrolls by. For me during this install the grand total is:
773M

Hey thats not bad! 773MB for the entire rails/mysql/lighttpd stack with subversion and postfix installed and configured. If you did this on a 4Gig VPS then you still have over 3Gigs left for data and rails apps.
Now there is more you can do to tweak this system. Such as tuning mysql for performance with less memory. Depending on how much SQL your rails apps are going to be using you might want to run a few less mysql processes than the default. Or you might want to go with postgresql or sqlite. Thats a bit beyond how long I want to make this tutorial. But I hope this helps you get the most out of rails and a VPS server. I know that this is the way to go for hosting rails apps and developing them as well. Now you will have all your code that you have checked into svn available anywhere you go and your rails apps, email and other services will not go down or be affected by other users on the same shared host. This is a very nice environment because you know that you are the only one there and all the resources are yours. No beginners with mis-configured apps running 40 fcgi processes or anything else to ruin your day and your uptime. You will need to create new svn repos and since we don't have apache bloat installed the best way to run svn over the network is with svnserve or over svn+ssh. I'll leave that as an exercise for the reader as well as whatever else you want to do to customize your new $HOME.