Tommy Stanton

Computer programmer and banjo picker

nginx/Catalyst/cgit/FastCGI configuration for tommystanton.com

Getting nginx, Catalyst, and cgit (a web-based Git repository viewer) to work together was an absolute nightmare, but I prevailed. :)

nginx: Running cgit and a Catalyst application via FastCGI

$ sudo mkdir /var/log/nginx/tommystanton.com/

Here is most of my nginx configuration (from /etc/nginx/nginx.conf), which is inside of a 'server' block:

listen       80;
server_name  tommystanton.com;

# I don't like URLs that start with "www." :)
if ($host = 'www.tommystanton.com') {
    rewrite  ^/(.*)$  http://tommystanton.com/$1  permanent;
}

# Redirect requests for '/git' to '/cgit'
rewrite ^/git(/.+)?$    /cgit$1   permanent;
location /cgit {
    root /var/www/htdocs/cgit;

    # /cgit/ doesn't work
    rewrite ^/cgit/$    /cgit   permanent;

    # Use the FastCGI Unix socket provided by spawn-fcgi(1)
    fastcgi_pass  unix:/var/www/spawn-fcgi/tommystanton_com-cgit;

    include fastcgi_params-cgit;
}
# Fix the paths for cgit's few image and style files
location ~* ^/cgit/((?:img|css)/[^/]+\.(?:css|png|ico))$ {
    alias /var/www/htdocs/cgit/$1;
}

location / {
    # Use the FastCGI Unix socket provided by Catalyst
    fastcgi_pass  unix:/var/www/htdocs/tommystanton.com/blog/fastcgi.socket;

    include fastcgi_params-Catalyst;
}

The include'd /etc/nginx/fastcgi_params-cgit file (based on Haering's 'server' section):

fastcgi_read_timeout    5m;
# XXX Not sure why two slashes are necessary, but one didn't quite do it
fastcgi_index   //;

fastcgi_param    DOCUMENT_ROOT    /var/www/htdocs/cgit;
fastcgi_param    SCRIPT_FILENAME  /var/www/htdocs/cgit/cgit;
fastcgi_param    QUERY_STRING  $query_string;
fastcgi_param    REQUEST_METHOD  $request_method;
fastcgi_param    CONTENT_TYPE  $content_type;
fastcgi_param    CONTENT_LENGTH  $content_length;
fastcgi_param    GATEWAY_INTERFACE  CGI/1.1;
fastcgi_param    SERVER_SOFTWARE  nginx;
## XXX 'cgit' without the leading forward slash doesn't work
#fastcgi_param    SCRIPT_NAME  /cgit;
fastcgi_param    SCRIPT_NAME  $fastcgi_script_name;
fastcgi_param    REQUEST_URI  $request_uri;
fastcgi_param    DOCUMENT_URI  $document_uri;
fastcgi_param    DOCUMENT_ROOT  $document_root;
fastcgi_param    SERVER_PROTOCOL  $server_protocol;
fastcgi_param    REMOTE_ADDR  $remote_addr;
fastcgi_param    REMOTE_PORT  $remote_port;
fastcgi_param    SERVER_ADDR  $server_addr;
fastcgi_param    SERVER_PORT  $server_port;
fastcgi_param    SERVER_NAME  $server_name;

The include'd /etc/nginx/fastcgi_params-Catalyst file (taken from a Catalyst Advent article):

fastcgi_param  QUERY_STRING       $query_string;
fastcgi_param  REQUEST_METHOD     $request_method;
fastcgi_param  CONTENT_TYPE       $content_type;
fastcgi_param  CONTENT_LENGTH     $content_length;

# Catalyst requires setting PATH_INFO (instead of SCRIPT_NAME) to $fastcgi_script_name
fastcgi_param  PATH_INFO          $fastcgi_script_name;
fastcgi_param  SCRIPT_NAME        /;
fastcgi_param  REQUEST_URI        $request_uri;
fastcgi_param  DOCUMENT_URI       $document_uri;
fastcgi_param  DOCUMENT_ROOT      $document_root;
fastcgi_param  SERVER_PROTOCOL    $server_protocol;

fastcgi_param  GATEWAY_INTERFACE  CGI/1.1;
fastcgi_param  SERVER_SOFTWARE    nginx/$nginx_version;

fastcgi_param  REMOTE_ADDR        $remote_addr;
fastcgi_param  REMOTE_PORT        $remote_port;
fastcgi_param  SERVER_ADDR        $server_addr;
fastcgi_param  SERVER_PORT        $server_port;
fastcgi_param  SERVER_NAME        $server_name;

cgit: Configuration

/etc/cgitrc needs to be updated in a few small places, to account for the '/cgit/' URI:

diff --git a/cgitrc b/cgitrc
index 81ef1f0..7ad1353 100644
--- a/cgitrc
+++ b/cgitrc
@@ -9,7 +9,7 @@ cache-size=1000
 #clone-prefix=git://example.com ssh://example.com/pub/git http://example.com/git

 # Specify the css url
-css=/cgit-data/cgit.css
+css=/cgit/css/cgit.css

 # Show extra links for each repository on the index page
 enable-index-links=1
@@ -24,7 +24,7 @@ enable-log-linecount=1
 #favicon=/favicon.ico

 # Use a custom logo
-logo=/cgit-data/cgit.png
+logo=/cgit/img/cgit.png

 # Set the title and heading of the repository index page
 #root-title=example.com git repositories

fcgiwrap and spawn-fcgi: Funneling CGI to FastCGI

cgit is only designed to be run as a CGI application. nginx doesn't do CGI (only FastCGI), so another set of programs must be used (in this case, fcgiwrap and spawn-fcgi) to act as a wrapper to be able to run a CGI application under FastCGI.

Luckily, I had some hints on how to go about this after going through a blog post by Russell Haering, but it still took me countless hours of trial and error to get everything to work.

fcgiwrap is only available via compiling the source from its Git repository on GitHub. Here is the way I had to go about it, since I'm on CentOS and I can't get the newest libraries needed to compile the current source; I had to use the source tree at a specific commit state:

$ git clone https://github.com/gnosek/fcgiwrap.git
$ git checkout 28ac6f9aa5e7dadf9aaf2062ab003a0fb4c82ad8
$ make && sudo cp -v ./fcgiwrap /usr/local/bin/

There seem to be two projects/programs out there called "spawn-fcgi": one is simply a Perl script (I think it's from the nginx community) and the other is a compiled binary (written in C). I used the spawn-fcgi binary, which I could easily install via yum(1).

/etc/sysconfig/spawn-fcgi (initially provided by the RPM package via yum; here is where I make use of fcgiwrap):

# You must set some working options before the "spawn-fcgi" service will work.
# If SOCKET points to a file, then this file is cleaned up by the init script.
#
# See spawn-fcgi(1) for all possible options.
#
# Example :
#SOCKET=/var/run/php-fcgi.sock
#OPTIONS="-u apache -g apache -s $SOCKET -S -M 0600 -C 32 -F 1 -P /var/run/spawn-fcgi.pid -- /usr/bin/php-cgi"

DIR=/var/www/spawn-fcgi
SOCKET="$DIR/tommystanton_com-cgit"
OPTIONS="-d $DIR -u nginx -g nginx -s $SOCKET -S -M 0777 -F 1 -P /var/run/spawn-fcgi.pid -- /usr/local/bin/fcgiwrap"

Galuga: Create a user with its own perl

In order to make the Catalyst application (Galuga) a self-sufficient service on the GNU/Linux box, it is best to create an account for it and to give it its own Perl-related resources.

The two tools used for the Perl part are perlbrew (used to install a local perl) and cpanminus (used to install CPAN modules).

$ /bin/su -
Password: 
# useradd galuga
# exit
$ sudo /bin/su - galuga
$ curl -L http://xrl.us/perlbrewinstall | bash # Bootstrap perlbrew
$ echo source ~/perl5/perlbrew/etc/bashrc >> ~/.bashrc
$ exit
$ sudo /bin/su - galuga
$ perlbrew install perl-5.12.1 # This will take a while...
$ perlbrew switch perl-5.12.1
$ curl -L http://cpanmin.us | perl - App::cpanminus # Bootstrap cpanminus
$ cd /var/www/htdocs/tommystanton.com/blog/ # Where my Galuga resides
$ cpanm Module::Install::Catalyst && \
cpanm --installdeps . # This will also take a while...

Galuga: Setup the init script

Use galuga_fastcgi_server.sh as an init script, via a symlink:

$ cd /etc/init.d/ && \
sudo ln -sv ./galuga /var/www/htdocs/tommystanton.com/blog/script/galuga_fastcgi_server.sh

Checking init

Now that all is setup, make sure that the required services automatically start on the standard runlevels (alternatively, use something like update-rc.d(8) on a Debian-based machine):

$ for service in nginx galuga spawn-fcgi; do
sudo chkconfig --level 2345 $service on
done
blog comments powered by Disqus