Running Mercurial with FastCGI in nginx

29 07 2009

logo-droplets-200 Mercurial is a so called DRCS (Distributed Revision Control System). I have been using Subversion for a couple of years, both at work and for my own projects. Now I thought it was about time to try something different.

But first, why do I want to switch from SVN to Mercurial? Basically the most appealing argument for me was the fact, that with Mercurial I am able to work offline with my repository. Besides that, I always had issues with the way SVN was handling tags and branches. Especially merging changes from a branch back into the trunk was always a pain. I did not need to use that functionality often but when I did, I always ended up doing it twice, because I could not remember which way to do it right.

So, in this article I will describe my setup of Mercurial served via FastCGI behind the nginx webserver. The approach is similar to the integration of PHP into nginx. You need the spawn-fcgi tool from the lighttpd distribution. The following steps should work on a recent Debian or Ubuntu distribution.

The first step is to install Mercurial and some necessary libraries to create the fcgi-wrapper for Mercurial:

aptitude install mercurial python-flup

Now you can already start defining your Mercurial repositories. Here are some steps to create a small example repository:

cd /tmp
hg init hgtest
cd hgtest
echo "Hello world." > readme.txt
hg add readme.txt
hg commit -m "Initial commit"

You now have a Mercurial repository with a single file. The command hg log should show you a single changeset with our commit comment.

Now, let’s start configuring nginx to integrate Mercurial. We will use FastCGI talk connect Mercurial to nginx. User authentication will be done via nginx. It is a good idea to use HTTPS for communication with Mercurial, but we will focus on a standard HTTP setup.

Let’s setup a virtual host for Mercurial. Open /etc/nginx/sites-available/your_domain_name and add the following server definition:

server {
        listen 80;
        server_name YOUR_MERCURIAL_DOMAIN;
 
        location / {
                auth_basic "Secure Login";
                auth_basic_user_file /tmp/mercurial_users;
                fastcgi_pass 127.0.0.1:9001;
                fastcgi_param SCRIPT_FILENAME /tmp$fastcgi_script_name;
                fastcgi_param PATH_INFO $uri;
                fastcgi_param REMOTE_USER $remote_user;
                include fastcgi_params;
        }       
}

The above settings will setup a new virtual host, where all traffic is redirected to the Mercurial FastCGI wrapper. It is important that you forward the PATH_INFO and REMOTE_USER variables. Mercurial will not work correctly without these.

Now reload the nginx configuration:

/etc/init.d/nginx reload

And create the password file:

htpasswd -c /tmp/mercurial_users MYLOGIN

Mercurial uses a central configuration file. In this file we can specify locations for our mercurial repositories. The following file will enable all mercurial repositories found in the /tmp directory. It also changes the theme to gitweb which is a bit clearer than the default theme. Create the file /tmp/hgweb.config with the following contents:

[collections]
/tmp = /tmp
 
[web]
style = gitweb
baseurl =

Mercurial uses a second configuration file for each repository where you may specify details about the repository and security settings like who may push changes into the repository. The configuration should be placed into the file /tmp/hgtest/.hg/hgrc and could look like this:

[web]
contact = YOUR NAME
description = DESCRIPTION OF PROJECT
style = gitweb
push_ssl = false
allow_archive = bz2 gz zip
allow_push = LOGIN_NAME

Now we need to grab the following script from the Mercurial repository and place it into the /tmp directory: http://selenic.com/repo/hg/raw-file/tip/contrib/hgwebdir.fcgi. Now edit this file and replace the line WSGIServer(hgwebdir('hgweb.config')).run() with WSGIServer(hgwebdir('/tmp/hgweb.config')).run().

The last step is to set the correct filesystem rights for your repository:

chown -R www-data.www-data /tmp/hgtest

That’s it. Now we can start the FastCGI process via:

spawn-fcgi -a 127.0.0.1 -p 9001 -u www-data -g www-data -f /tmp/hgwebdir.fcgi -P /var/run/fastcgi-mercurial.pid -C 1

It makes sense to write the above line into /etc/rc.local so that it will start up automatically when you reboot the server.

Further information about configuring your Mercurial server can be found in the Mercurial Wiki.


Actions

Informations

8 responses to “Running Mercurial with FastCGI in nginx”

9 09 2009
Michael Papenbrock (14:00:12) :

This looks quite interesting and I am eager to give it a try. However, in terms of security I guess you are also using fail2ban for this :)
Is it sufficient just do create a jail for nginx or is there more one should consider?

Cheers,
Michael

9 09 2009
Peter (15:12:11) :

Yes, I am indeed using fail2ban to secure my repository. I have defined a new filter file /etc/fail2ban/filter.d/nginx-auth.conf to identify failed login attempts in the nginx access log (note: nginx does not log failed attempts to the error.log like Apache):

[Definition]
failregex = <HOST>.*" 401

This filter is then called from a custom jail definition:

[mercurial]
 
enabled = true
port = https
filter = nginx-auth
logpath = /var/log/nginx/access.log
maxretry = 3

So far this is working out nice.

14 09 2009
Markus Papenbrock (16:53:53) :

I ran into problems where an empty repository list was shown.
To solve this, you have to edit the file /tmp/hgwebdir.fcgi
Replace this line at the bottom:
WSGIServer(hgwebdir('hgweb.config')).run()
with a direct reference to the hgweb.config file:
WSGIServer(hgwebdir('/tmp/hgweb.config')).run()

14 09 2009
Peter (16:58:18) :

Thanks, I forgot to mention this modification in my howto. The posting is now updated.

15 09 2009
Michael Papenbrock (11:01:49) :

It’s running!!! Thank you very much!

17 10 2009
seth (01:06:16) :

After running

spawn-fcgi -a 127.0.0.1 -p 9001 -u www-data -g www-data -f /tmp/hgwebdir.fcgi -P /var/run/fastcgi-mercurial.pid -C 1

I get the following

spawn-fcgi: child exited with: 2

Any thoughts?

17 10 2009
seth (04:12:02) :

To answer my own problem.

The executable bit needs to be set on hgwebdir.fcgi
(chmod +x hgwebdir.fcgi)

Otherwise we get:
spawn-fcgi: child exited with: 2

Also (I could be incorrect), but instead of:
chmod -R www-data.www-data /tmp/hgtest

I think it was meant to be:
chown -R www-data.www-data /tmp/hgtest

Thanks for the great post!

17 10 2009
Peter (10:24:24) :

You are right. Thanks for pointing out the error!

I updated the posting.

Leave a comment

You can use these tags : <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="">

For spam filtering purposes, please copy the number 3995 to the field below: