Nginx and Mercurial setup

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


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
<Location /hg>
   AuthType Basic
   AuthName "Mercurial repositories"
   AuthUserFile /var/hg/hgusers
   Require valid-user

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


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/", 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;
   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;
   client_max_body_size 20M;


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

Now it become a full virtual host and changed to:

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.


413 Request Entity Too Large

Did you find this post useful? Support the the author ($10)
My Google Profile+

1 comment

  1. Hey A32, what made you change from Apache? I’m still struggling to get my setup working and am still using Apache. I am a little tempted to try this with Nginx, but at the same time I don’t changing just because I can’t fix my apache issue (push auth is denied).

Leave a Reply

Your email address will not be published.