Setup Mercurial and HgWeb on OpenBSD

8 Aug 2018

Recently I moved my personal repositories from Git to Mercurial, mostly out of curiosity to try something new. It went well, and I thought that it would be nice to have these repos synced remotely and also exposed on the web in read-only mode. I had one OpenBSD server already, the one that powers this blog, so it made sense to me to set up Mercurial server and HgWeb there as well. What seemed like a straightforward task turned out to be a bit more complicated; in order to not forget all the useful information I learned while solving it, this article appeared.

I use OpenBSD 6.3 in the article; for other versions things might be slightly different.


I assume that you've already set up DNS for the server. Here and further I'm going to use as a server name.

Don't forget to update your OpenBSD installation with the newest patches before proceeding:

# syspatch

Setting up Mercurial server

Install Mercurial:

# pkg_add mercurial

Create separate user that will interact with repos:

# useradd -v -m hg
# passwd hg

That's it. After adding SSH keys to hg user you can create, pull and push repositories from the server:

$ ssh 'hg init test-repo'
$ hg clone ssh://
... make and commit changes ..
$ hg push ssh://

Setting up HgWeb

HgWeb is a CGI-application, so we need an external HTTP server in order to run it on the web. Luckily OpenBSD contains simple yet powerful HTTP daemon httpd by default, but it supports only FastCGI apps. There is another daemon, slowcgi, that is an adapter for CGI apps; it's tricky, however, to use it for apps in Python or other interpreted languages because it chroots the app to /var/www, so in order for it to run the app you need to copy each and every library and executable that it depends on, as well as to remount /var as wxallowed.

There is another solution though: we can use flup Python package to convert HgWeb app between CGI and FastCGI. Let's install it:

# pkg_add py-flup

Then copy HgWeb script and edit it:

$ cp /usr/local/share/mercurial/hgweb.cgi /home/hg/

Here is how my HgWeb script looks like:


# Path to repo or hgweb config to serve (see 'hg help hgweb')
config = "/home/hg/hgweb.conf"

from mercurial import demandimport; demandimport.enable()
from mercurial.hgweb import hgweb
from flup.server.fcgi import WSGIServer
application = hgweb(config)
WSGIServer(application, bindAddress=("", 9001)).run()

The script will spawn FastCGI server on Now let's configure httpd to send all requests to the FastCGI app. Open /etc/httpd.conf in your favorite editor and add the following lines:

server "" {
    listen on * port 80
    fastcgi socket ":9091"

Finally let's configure HgWeb itself by creating config file /home/hg/hgweb.conf and editing it:1

username = Nikolai Obedin <>

/ = /home/hg/repos/*

highlight =

highlightfiles = size('<1M')
allow_archive = gz, zip
logourl =

In order for the highlighter to work, you need to have Pygments installed:

# pkg_add py-pygments

Now it all works well, but it would be even better if we could start, stop and query status of HgWeb without wrapping it in screen or something similar, so let's write a daemon for it. OpenBSD daemons are just shell scripts having specific structure 2 They reside in /etc/rc.d/ directory. Let's create a new file there, hgwebd, and edit it:




. /etc/rc.d/rc.subr

# Regexp used in pgrep/pkill to ry status/stop the daemon

# Disable `rcctl reload` command

# Daemonize the process because doesn't do it by itself

rc_cmd $1

After makinig it executable you can start, stop or check HgWeb status simply by running rcctl commands.


Though setting up HgWeb was more involving than e.g. CGit, it didn't take a lot of time, thanks to the clear and extremely helpful documentation of OpenBSD. I really like the approach of lesser features and simpler configurations that OpenBSD team employs; it makes system administration easier and safer overall.

  1. Unfortunately there is no single source of documentation for HgWeb configuration. The best resource that I found is