To you visitor, an elated 'Hello' from "Lighty" - as it is so fondly referred to. Thats right, my server greeted you not just with the standard apache service, but instead with both apache and Lighttpd. How could this be? And most importantly, why do I have apache2 and lighttpd running on the same server? Well, listen close and I will divulge the recent happenings surrounding my server and my websites.
Well it all started with this innocent periodically_call_remote function I had on my main page here. It was set to call back for a snippet of data every 8 seconds. Nothing too complex was ever returned (at most about 3-4 lines of plain text). Things were going great until I started to get a few visitors on my site, and some of them seemed to leave the tab on my main page. After a few dozen people had forthecode.com open in their browser, all with the periodically_call_remote playing pitch and catch with my server, things started to really slow down. Why?
Apache2 does not run fcgi ( fast cgi), although it supports its version of fcgid, it is still a bit unreliable (and often buggy).
Thus apache loads and unloads the (un-fast) cgi processor each time a request is made, just to service that request! (slow!!!!)
I like to think that baking cookies would have a correlation to the way my apache2 configuration handled my Rails apps. Each request for page (a pan of delicious chocolate chip cookies) required that Apache heat up the oven (load the cgi processor), bake and serve the cookies (send the processed page), then cool the oven completely down before cooking another batch (unload the cgi processor). What a waste of time and processing power.
I took a screenshot of my processes at one of the slow hours- right when I really didn't have a whole lot of hits. In most scenarios my load was around 10+... quite frightening:

So, what did I do to fix the problem? Well in the simple world I would've just installed Lighttpd or Mongrel and ditched Apache (and probably added more memory to my machine). However, it wasn't so simple as a few sites on my server were quite happy with Apache, and I didn't feel like making a complete swap. Instead I thought it best I try running both packages side by side - one controlling all the initial connections (Apache2) and the other taking over where it is better suited (Lighttpd for Rails - FastCGI!).
I took the liberty of wget 'ing Lighttpd and installing it following the instructions on their site www.lighttpd.net. The simple portion of the experience at that moment passed away. For quite some time (several hours spread out over 3 days) I fought with Apache's apache.conf and vhost file - and likewise tangled with Lighty's lighttpd.conf file.
I spent quite a bit of time trying to find how to setup Apache2 as a front-end proxy that could serve requests to Lighttpd without breaking anything. So, let me quickly divulge what I learned:
- Duncan Davidson's essay is a great reference
- Be sure to check the Apache 2 specs on Proxypass
- Use Lighty's debug.log-* options to help diagnose any problems
- Remember to 'touch' and 'chmod' /path-to-domain-directory/log/fastcgi.crash.log
- Make certain your dispatch.fcgi has the proper she-bang (#!/usr/bin/ruby - in my case)
- Fall not into the trap of leaving out one simple set of Apache settings
- Don't underestimate the importance of Lighttpd's server.error-handler-404 directive in your config
Where do I start - easy... at the top of the list. For brevity's sake - check the top 3 out yourself - they are handy resources, with the first being perhaps the most particular to what I am discussing here. If you follow Duncan's essay to a T then you will most likely run into very few (if any) gotchas.
Number 4 and 5 are quite similar in nature. These are just things to keep in mind so that you dont overlook any potential small problems that seem to gum everything up. Make certain that your errorlog for fastcgi is available and writeable, also make certain that your dispatch.fcgi (found in your public directory -- my case /web) is correct and also executable (chmod 655).
Number 6 on the list really requires a bit more detail. There are several things you will have to do for Apache to correctly work with Lighttpd - let me hit the highlights by giving you generous snippets from my configs:
ProxyRequests Off
<Proxy *>
Order deny,allow
Allow from all
</Proxy>
ProxyRequests should be turned off for added protection from undesired proxying (others using your machine to hide themselves as they route to other locations). We are configuring a Reverse Proxy, so no need in it being on - so set it to Off to be certain. The one snippet that gave me problems when left out was the < Proxy *>...</Proxy> settings. Be sure to include them.
In your virtual hosts file (generally found either in a vhosts folder, or in /etc/apache2/sites-enabled) you will need to make a few domain specific changes. An example out of my configs:
<VirtualHost 192.168.0.100:80>
ServerName www.forthecode.com:80
DocumentRoot /home/www/www.forthecode.com/web/
ErrorLog /var/log/apache2/error.log
ProxyPass / http://www.forthecode.com:7410/
ProxyPassReverse / http://www.forthecode.com:7410
ProxyPreserveHost On
<Directory /home/www/www.forthecode.com/>
Options ExecCGI FollowSymLinks
AddHandler cgi-script .cgi
AllowOverride all
Order allow,deny
Allow from all
</Directory>
</VirtualHost>
The parts you will add are in bold (excluding the file path at the top) - easy enough.. Apache is now configured - and once we have lighttpd configured and running we can restart Apache to see it all work!
So on to point number 7 - be aware that the "server.error-handler-404" directive in your Lighttpd config is especially important. When Lighty receives the proxied (forwarded) request to respond to '/' or the root index for the site - it isn't much of a problem. Lighttpd simply calls on dispatch.fcgi to pull out whatever you have routed for your index page. However when the request to Lighttpd is something like:
http://www.forthecode.org/user/comment/1
It does something quite practical behind the scenes to try and match the address to a file / page. Lighty trys to match the GET with a physical address on your server. When using the debug.* directives you get to see this interesting sequence of events - one that seems to end in disaster for our rails app:
2006-08-07 22:03:00: (response.c.175) -- splitting Request-URI
2006-08-07 22:03:00: (response.c.176) Request-URI : /user/comment/1
2006-08-07 22:03:00: (response.c.177) URI-scheme : http
2006-08-07 22:03:00: (response.c.178) URI-authority: www.forthecode.org:7410
2006-08-07 22:03:00: (response.c.179) URI-path : /user/comment/1
2006-08-07 22:03:00: (response.c.180) URI-query :
2006-08-07 22:03:00: (response.c.230) -- sanatising URI
2006-08-07 22:03:00: (response.c.231) URI-path : /user/comment/1
2006-08-07 22:03:00: (response.c.303) -- before doc_root
2006-08-07 22:03:00: (response.c.304) Doc-Root : /home/www/www.forthecode.org/web/
2006-08-07 22:03:00: (response.c.305) Rel-Path : /user/comment/1
2006-08-07 22:03:00: (response.c.306) Path :
2006-08-07 22:03:00: (response.c.353) -- after doc_root
2006-08-07 22:03:00: (response.c.354) Doc-Root : /home/www/www.forthecode.org/web/
2006-08-07 22:03:00: (response.c.355) Rel-Path : /user/comment/1
2006-08-07 22:03:00: (response.c.356) Path : /home/www/www.forthecode.org/web/user/comment/1
2006-08-07 22:03:00: (response.c.373) -- logical -> physical
2006-08-07 22:03:00: (response.c.374) Doc-Root : /home/www/www.forthecode.org/web/
2006-08-07 22:03:00: (response.c.375) Rel-Path : /user/comment/1
2006-08-07 22:03:00: (response.c.376) Path : /home/www/www.forthecode.org/web/user/comment/1
2006-08-07 22:03:00: (response.c.393) -- handling physical path
2006-08-07 22:03:00: (response.c.394) Path : /home/www/www.forthecode.org/web/user/comment/1
2006-08-07 22:03:00: (response.c.430) -- file not found
2006-08-07 22:03:00: (response.c.431) Path : /home/www/www.forthecode.org/web/user/comment/1
As you can see - Lighty takes the GET request for /user/comment/1 and trys to match it with the physical directory structure presented on the server... unfortunately it doesn't even talk to rails and instead decides to bomb out with a 404 Not Found error page. So, here is where the configuration command I just mentioned is so important. Instead of allowing the 404 not found page to display- you set this in your config to make things happen:
server.error-handler-404 = "/dispatch.fcgi"
This tells Lighty to use the dispatch.fcgi found in the virtual document root to handle all 404 errors. And in this case - the 404 error is stifled as dispatch.fcgi knows exactly what to do with this GET request - hand it over to your Rails app to process. Thus to continue on the request above, after setting the error-handler-404 in your config you should see this result:
2006-08-07 22:03:00: (response.c.175) -- splitting Request-URI
2006-08-07 22:03:00: (response.c.176) Request-URI : /dispatch.fcgi
2006-08-07 22:03:00: (response.c.177) URI-scheme : http
2006-08-07 22:03:00: (response.c.178) URI-authority: www.forthecode.org:7410
2006-08-07 22:03:00: (response.c.179) URI-path : /dispatch.fcgi
2006-08-07 22:03:00: (response.c.180) URI-query :
2006-08-07 22:03:00: (response.c.230) -- sanatising URI
2006-08-07 22:03:00: (response.c.231) URI-path : /dispatch.fcgi
2006-08-07 22:03:00: (response.c.303) -- before doc_root
2006-08-07 22:03:00: (response.c.304) Doc-Root : /home/www/www.forthecode.org/web/
2006-08-07 22:03:00: (response.c.305) Rel-Path : /dispatch.fcgi
2006-08-07 22:03:00: (response.c.306) Path :
2006-08-07 22:03:00: (response.c.353) -- after doc_root
2006-08-07 22:03:00: (response.c.354) Doc-Root : /home/www/www.forthecode.org/web/
2006-08-07 22:03:00: (response.c.355) Rel-Path : /dispatch.fcgi
2006-08-07 22:03:00: (response.c.356) Path : /home/www/www.forthecode.org/web/dispatch.fcgi
2006-08-07 22:03:00: (response.c.373) -- logical -> physical
2006-08-07 22:03:00: (response.c.374) Doc-Root : /home/www/www.forthecode.org/web/
2006-08-07 22:03:00: (response.c.375) Rel-Path : /dispatch.fcgi
2006-08-07 22:03:00: (response.c.376) Path : /home/www/www.forthecode.org/web/dispatch.fcgi
2006-08-07 22:03:00: (response.c.393) -- handling physical path
2006-08-07 22:03:00: (response.c.394) Path : /home/www/www.forthecode.org/web/dispatch.fcgi
2006-08-07 22:03:00: (response.c.401) -- file found
2006-08-07 22:03:00: (response.c.402) Path : /home/www/www.forthecode.org/web/dispatch.fcgi
2006-08-07 22:03:00: (response.c.520) -- handling subrequest
2006-08-07 22:03:00: (response.c.521) Path : /home/www/www.forthecode.org/web/dispatch.fcgi
2006-08-07 22:03:00: (mod_fastcgi.c.3144) handling it in mod_fastcgi
2006-08-07 22:03:00: (response.c.102) Response-Header:
HTTP/1.1 200 OK
Congrats - it appears to be working (once you have gotten to this point). So without further ado - allow me to post my current Lighttpd config as a guide to what you need to setup multiple domains behind the Apache2 proxy:
# lighttpd configuration file
#
# Modified by Charles Abbott - www.forthecode.com 2006
##########################################
server.modules = (
"mod_rewrite",
"mod_access",
"mod_fastcgi",
"mod_simple_vhost",
"mod_accesslog" )
## a static document-root, for virtual-hosting take look at the
## server.virtual-* options
server.document-root = "/home/www/www.forthecode.com/web/"
## where to send error-messages to
server.errorlog = "/var/log/lighttpd/error.log"
# files to check for if .../ is requested
server.indexfiles = ( "dispatch.fcgi", "index.html" )
#### accesslog module
accesslog.filename = "/var/log/lighttpd/access.log"
accesslog.format = "%{X-Forwarded-For}i %h %l %u %t \"%r\" %b %>s \"%{User-Agent}i\" \"%{Referer}i\" \"%{X-LIGHTTPD-SID}o\""
## deny access the file-extensions
url.access-deny = ( "~", ".inc" )
## bind to port (default: 80)
server.port = 7410
## error-handler for status 404
server.error-handler-404 = "/dispatch.fcgi"
## to help the rc.scripts
server.pid-file = "/var/run/lighttpd.pid"
###### virtual hosts
##
## If you want name-based virtual hosting add the next three settings and load
## mod_simple_vhost
##
## document-root =
## virtual-server-root + virtual-server-default-host + virtual-server-docroot or
## virtual-server-root + http-host + virtual-server-docroot
##
simple-vhost.server-root = "/home/www/"
simple-vhost.default-host = "www.forthecode.com"
simple-vhost.document-root = "web"
$HTTP["host"] == "www.forthecode.com" {
server.document-root = "/home/www/www.forthecode.com/web"
fastcgi.server = ( ".fcgi" =>
( "localhost" =>
(
"min-procs" => 2,
"max-procs" => 4,
"socket" => "/tmp/forthecodecom.socket",
"bin-path" => "/home/www/www.forthecode.com/web/dispatch.fcgi"
)
)
)
}
$HTTP["host"] == "www.forthecode.org" {
server.document-root = "/home/www/www.forthecode.org/web"
fastcgi.server = ( ".fcgi" =>
( "localhost" =>
(
"max-procs" => 1,
"socket" => "/tmp/forthecodeorg.socket",
"bin-path" => "/home/www/www.forthecode.org/web/dispatch.fcgi"
)
)
)
}
# HTTP-header headers to error-log
#UNCOMMENT THESE FOR DEBUGGING
#debug.log-file-not-found = "enable"
#debug.log-request-header = "enable"
#debug.log-response-header = "enable"
#debug.log-request-handling = "enable"
## change uid to
server.username = "www-data"
## change uid to
server.groupname = "www-data"
##Mimetype mapping
#(lots of mime stuff here - get it from the standard Lighttpd config)
Thanks to this configuration my server is running much faster than it was before. Pages are displayed in a fraction of the time - my personal tests showed that before with Apache2 alone page access was about 15 seconds, now with Apache2 proxying to Lighttpd each page can be accessed in under 3 seconds! Now that is a major productivity increase!

Charles said:
As a note- having
server.document-root = "/home/www/www.forthecode.com" inside the $HTTP[]{... blocks ...} really doesnt appear to do anything - since mod simple_vhost overwrites it anyway (this is also stated in lighttpd's documentation)
I have since removed this from my config.
2006-08-17 17:59:05 UTCCharles said:
Also - Lightty behind my Apache server doesn't play nice with Dir.pwd or gwd Ruby methods.
The upload model I wrote previously had problems moving data from my /tmp directory to the working directory of my Rails app.
The reason? -- This setup caused my File.Mv and gwd commands do something like this:
/tmp/CGI00021.tmp -> //uploads/images/newfile.png
Notice the // -- it dropped the full directory path to the file (/home/www/www.forthecode.com/web/uploads/...)
My quick fix was hardcoding the full path in my model. Once I get some more time to debug this problem I'll write up a more complete solution.
2006-08-17 21:10:54 UTCBud said:
I experienced your site before and after you went to Lightty. With my rails application running on Ubuntu I did get a similar boost in speed by just going to apache2's fcgid. Did you attempt to use apache2's fcgid prior to implementing Lightty? I have not seen any problems (yet) with fcgid, but am troubled by your initial comments in this article.
2006-10-26 19:16:19 UTCCharles said:
Bud, I recall looking into apache2's fcgid but found alot of write-ups stating that it was still a bit buggy (many of them talking about zombied processes and occassional hang-ups). To quote Duncan Davidson's essay on the same matter:
"Why is it problematic? Well, I've worked with several configurations of FastCGI under Apache. And every time I think I've got things worked out just right, I'll find a bunch of zombie Rails processes."
So, I took the easier route (or what I thought would be the easier route) and jumped on the lighttpd bandwagon. If you have had success with fcgid then thats great! Keep me posted if you ever have any problems (or if you dont). I personally have always liked working with apache - mainly because I am used to it.
2006-10-27 08:07:24 UTCBud said:
Thanks for elaborating.
2006-10-27 10:06:29 UTC