Catalyst with Lighttpd

I spent most of last week preparing a new server to deploy a project onto a nice shiny new 1&1 Dedicated Server (oh and if you buy something from that link I get a referral bonus - yay!) All the boring stuff like bootstrapping a fresh Debian install onto the disks from a rescue image; fighting the bootloader to make it work with no indication of errors when I make a mistake since I have no physical access to the machine - you know: the ususal.

So once I'd finished those mundane tasks I had to setup the webserver. Previously I'd have gone straight for Apache 2. However I'm deploying a Catalyst application to the machine, and although Catalyst will run under mod_perl, the recommended method is to use FastCGI since it turns out to be a lot less hassle in the long run.

Aims with FastCGI

What I want from my FastCGI deployment:

  • to have two seperate FastCGI managers running, so that whenever I need to upgrade the code, I can shut one down, bring it back up with the new code, then do the same for the other - thus giving me the ability to upgrade the code with no downtime (assuming of course that there are no DB changes that might require downtime etc. etc.)
  • to have the static content served via the webserver, since this is what they do well
  • to not have fastcgi show up anywhere in the URL.

Apache...?

Doing this with Apache+mod_fastcgi seems impossible though, since Apache's handling of FastCGI isn't briliant. To do this in Apache you basically have to create two vhosts (and optionally give them ServerName's that dont resolve externally) and then Alias / /path/to/fastCGI/N.socket.

Still with me? 'cos we're not done yet. In the main vhost for the server (i.e. www.myapp.com) you then have to create a balanced proxy like the following:

ProxyPass / balancer://myapp-cluster
<Proxy balancer://myapp-cluster>
  BalanceMember http://myapp-1.internal
  BalanceMember http://myapp-2.internal
  Allow from all
  Order Allow,Deny
</Proxy>

Complex, no? And whats worse, is that it doesn't even cope with one of the backend FastCGI servers being down - you just get a 500 page served to the user. Bad Apache! Try the other (FastCGI) server please!

The Solution - Lighttpd

After a bit of Googling, it seems like Apache just wont cope with this situation. I've been hearing good things about Lighttpd, particularly that its FastCGI support is native and much better.

The interesting bits of lighttpd config I've used:

server.moudles += (
    "mod_alias",
    "mod_rewrite",
    "mod_redirect",
    "mod_setenv",
)

Pretty self explanatory - load some of the modules we need. The rest of the config goes inside the $HTTP["host"] =~ "www.mysite.com" directive.

    # Let lighttpd take care of serving that static content
    alias.url = (
      "/favicon.ico" => "/var/www/mysite.com/MySite/root/favicon.ico",
      "/js/"   => "/var/www/mysite.com/MySite/root/js/",
      "/css/"  => "/var/www/mysite.com/MySite/root/css/",
      "/tour/" => "/var/www/mysite.com/MySite/root/tour/",
    )

    # rewrite-once will stop processing after the first match
    url.rewrite-once = (
      "^/((?:js|css|tour)/.*)" => "/$1",
      "^/favicon.ico" => "/favicon.ico",
      "^/(.*)" => "/fcgi/$1"            
    )
}

These two bits tell lighttpd to handle all the static content itself, and then forward everything else off to the /fcgi path, which is defined as follows:

  fastcgi.server = (
    "/fcgi" => (
      ( "host" => "127.0.0.1", "port" => 3010, "check-local" => "disable"),
      ( "host" => "127.0.0.1", "port" => 3011, "check-local" => "disable")
    )
  )

Fairly obvious - everything under /fcgi is handled by the FastCGI servers. Note however that this will make Catalyst think it is based at http://mysite.com/fcgi/ which it isn't, so we need to fix it:

    $HTTP["url"] =~ "^/" {
      setenv.add-environment = ( "SCRIPT_NAME" => "/" )
    }

That does the job - Catalyst now thinks its rooted at /, and lighttpd is now handling all the static content itself.

For the completeness-sake, I've included the full config as a single block below.

$HTTP["host"] =~ "^mysite.com" {
    url.redirect = ( "^/(.*)" => "http://www.mysite.com/$1" )
}

$HTTP["host"] =~ "www.mysite.com" {

    # dir listings are bad
    server.dir-listing = "disable"

    server.errorlog    = "/var/log/lighttpd/mysite.com.error.log"
    accesslog.filename = "/var/log/lighttpd/mysite.com.access.log"

    # Let lighttpd take care of serving that static content
    alias.url = (
      "/favicon.ico" => "/var/www/mysite.com/MySite/root/favicon.ico",
      "/js/"   => "/var/www/mysite.com/MySite/root/js/",
      "/css/"  => "/var/www/mysite.com/MySite/root/css/",
      "/tour/" => "/var/www/mysite.com/MySite/root/tour/",
    )

    # rewrite-once will stop processing after the first match
    url.rewrite-once = (
      "^/((?:js|css|tour)/.*)" => "/$1",
      "^/favicon.ico" => "/favicon.ico",
      "^/(.*)" => "/fcgi/$1"            
    )

    fastcgi.server = (
      "/fcgi" => (
        ( "host" => "127.0.0.1", "port" => 3010, "check-local" => "disable"),
        ( "host" => "127.0.0.1", "port" => 3011, "check-local" => "disable")
      )
    )

    $HTTP["url"] =~ "^/" {
      setenv.add-environment = ( "SCRIPT_NAME" => "/" )
    }

}

Last modified: Fri Nov 16 20:22:04 2007


  • Re: Catalyst with Lighttpd

    Written by Anonymous Coward (0) on Sun Sep 30 16:13:04 2007

    Since you aren't spawning the fastcgi instances with lighttpd, how do you manage taking down just one of the running fastcgi processes? Do you just manually kill it?

    I normally just spawn the processes from lighttpd (with the bin-path fastcgi option), but I guess that wouldn't work too well in your situation.

    • Re: Re: Catalyst with Lighttpd

      Written by Ash Berlin (bfc7465ebdca5337) [SIGNED] on Mon Oct 1 09:11:30 2007

      I use daemontools as described in the 2006 Catalyst Advent Calendar FastCGI Deployment with Bells on

      Basically I have

      /service/myapp-fcgi1
      /service/myapp-fcgi2
      

      Each run script is the same bar the port number it listens on. I tried to find a way to have the same service directory symlinked into /service twice and have the script detect which one it was invoked as, but it seems that daemontools does not provide this info (as it follows the symlinks properly before executing the run script)

      Pretty straight forward from then on.

  • Re: Catalyst with Lighttpd

    Written by Anonymous Coward (0) on Wed Jan 16 23:13:36 2008

    Great information! I'm currently trying to install lighttpd +FastCGI for Catalyst on my system and I'm getting hung up. You wouldn't happen to also have instructions for the basic install, would you?

  • Re: Catalyst with Lighttpd

    Written by Anonymous Coward (0) on Mon Jul 14 11:31:55 2008

    Thanks for breaking the ground with a lighttpd tutorial. Regarding static files, I ran into issues with Catalyst redirects, and with help from a bunch of folks on #catalyst, I put together a sleeker solution on the wiki at Deploying Catalyst applications with lighttpd and FastCGI Best regards, Dan Dascalescu