Some weeks ago I switched to Nginx and quit Apache and was quite happy with it. I was so happy that completely forgot about my Mercurial repositories hosted on same server. This happen when I was in hurry and needed to push and pull some changes,  damn.

At first I quickly restored Apache, did all needed changes and switched back to Nginx. For sure, I did not like this way and explored how I can connect Mercurial directly to Nginx. I was quite surprised how easy it is and how flexible.

# Mercurial via Apache setup

I had Mercurial setup through Apache in a very standard way.

/var/hg - Holding all my repositaries

/var/hg/hgweb.config

[collections]
repos/ = repos/

/var/hg/hgwebdir.cgi - For Apache CGI support

/var/hg/hguser - for user passwords in htpasswd format

Apache configuration was the following

ScriptAliasMatch ^/hg(.*) /var/hg/hgwebdir.cgi$1
<Directory /var/hg>
  Options ExecCGI FollowSymLinks
  AllowOverride None
</Directory>
<Location /hg>
   AuthType Basic
   AuthName "Mercurial repositories"
   AuthUserFile /var/hg/hgusers
   Require valid-user
</Location>

Switch to Nginx was really painless.

# Nginx and Mercurial setup

# Prepare Mercurial

First step I switched to FastCGI instead of CGI. To do this I copied file hgwebdir.fcgi to my repo root /var/hg and made it executable

copy /usr/share/doc/mercurial-common/examples/hgwebdir.fcgi /var/hg
chmod +x /var/hg/hgwebdir.fcgi

Or grab the file here

wget http://selenic.com/repo/hg/raw-file/tip/contrib/hgwebdir.fcgi

Next step is to install something that can spawn FastCGI processes, sine Nginx can not do this by itself.  This is easy with spawn-cgi

#apt-get install spawn-fcgi

I configured it via local unix socket instead of TCP port, it seems to me this way it is more secure. Run it like this:

#spawn-fcgi -u www-data -g www-data -s /tmp/mercurial.sock -d /var/hg/ -- /var/hg/hgwebdir.fcgi

At first start I've run into the problem

spawn-fcgi: child exited with: 1

After running with "-n" switch to see debug output, I  realised that I was missing one python module needed for a setup.

Traceback (most recent call last):
 File "/var/hg/hgwebdir.fcgi", line 25, in
 from flup.server.fcgi import WSGIServer
 File "/usr/lib/pymodules/python2.6/mercurial/demandimport.py", line 106, in _demandimport
 mod = _origimport(name, globals, locals)
 ImportError: No module named flup.server.fcgi

To fix this I installed missing python packages and issue was fixed.

#apt-get install python-flup

# Configure Nginx

Edit file /etc/nginx/sites-available/mercurial.conf and add the following server definition:

server {
   listen 80;
   server_name YOUR_MERCURIAL_DOMAIN;
   location / {
     auth_basic "Secure Login";
     auth_basic_user_file /var/hg/hgusers;
     include fastcgi_params;
     fastcgi_param PATH_INFO $uri;
     fastcgi_param REMOTE_USER $remote_user;
     fastcgi_intercept_errors on;
     fastcgi_pass unix:/tmp/mercurial.sock;
  } 
}

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.

Be sure that file /var/hg/hgusers contains all your Mercurial users with passwords, it can be created using htpasswd tool shipped with Apache. Restart Nginx and try to to push pull.

In case you have a big push load you may get an 413 Request Entity Too Large error. Just add client_max_body_size 20M; to "server" part of config, like this:

server {
   listen 80;
   server_name YOUR_MERCURIAL_DOMAIN;
   client_max_body_size 20M;

# Misc

One thing that changed was repositary path. Before it has /hg/ in path line

http://a32.me/hg/project1

Now it become a full virtual host and changed to:

http://hg.a32.me/project1

I have to reconfigure all my projects, but it is not a pain like it was with CVS.

Next thing I though about, I can move repositories under its own uid:gid and not run it under www-data, this can improve security.

Do not forget to include running spawn-fcgi into /etc/rc.local for automatically start on system boot.

My story is based on these links.

  • http://www.opennet.ru/base/dev/mercurial_http.txt.html
  • http://geeksharp.com/2010/01/20/mercurial-web-with-fastcgi-nginx/
  • http://fosswire.com/post/2009/08/hosting-mercurial-repositories-with-nginx/
  • http://www.softwareprojects.com/resources/programming/t-how-to-serve-mercurial-hg-repository-over-nginx-1856.html
  • http://www.dikant.de/2009/07/29/running-mercurial-with-fastcgi-in-nginx/

# 413 Request Entity Too Large