Serving via nginx on Debian and Ubuntu

Serving via nginx on Debian and Ubuntu

This document is an extension of the platform-independent SCGI instructions, which may suffice for your purposes if your needs are simple.

Here, we add more detailed information on nginx itself, plus details about running it on Debian type OSes. We focus on Debian 10 (Buster) and Ubuntu 18.04 here, which are common Tier 1 OS offerings for virtual private servers. This material may not work for older OSes. It is known in particular to not work as given for Debian 9 and older!

If you want to add TLS to this configuration, that is covered in a separate document which was written with the assumption that you’ve read this first.


This scheme is considerably more complicated than the standalone HTTP server and CGI options. Even with the benefit of this guide and pre-built binary packages, it requires quite a bit of work to set it up. Why should you put up with this complexity? Because it gives many benefits that are difficult or impossible to get with the less complicated options:

Fossil Service Modes

Fossil provides four major ways to access a repository it’s serving remotely, three of which are straightforward to use with nginx:

SCGI it is, then.

Installing the Dependencies

The first step is to install some non-default packages we’ll need. SSH into your server, then say:

   $ sudo apt install fossil nginx

Running Fossil in SCGI Mode

I run my Fossil SCGI server instances with a variant of the fslsrv shell script currently hosted in the Fossil source code repository. You’ll want to download that and make a copy of it, so you can customize it to your particular needs.

This script allows running multiple Fossil SCGI servers, one per repository, each bound to a different high-numbered localhost port, so that only nginx can see and proxy them out to the public. The “example” repo is on TCP port localhost:12345, and the “foo” repo is on localhost:12346.

As written, the fslsrv script expects repositories to be stored in the calling user’s home directory under ~/museum, because where else do you keep Fossils?

That home directory also needs to have a directory to hold log files, ~/log/fossil/*.log. Fossil doesn’t put out much logging, but when it does, it’s better to have it captured than to need to re-create the problem after the fact.

The use of --baseurl in this script lets us have each Fossil repository mounted in a different location in the URL scheme. Here, for example, we’re saying that the “example” repository is hosted under the /code URI on its domains, but that the “foo” repo is hosted at the top level of its domain. You’ll want to do something like the former for a Fossil repo that’s just one piece of a larger site, but the latter for a repo that is basically the whole point of the site.

You might also want another script to automate the update, build, and deployment steps for new Fossil versions:

   cd $HOME/src/fossil/trunk
   fossil up
   make -j11
   killall fossil
   sudo make install

The killall fossil step is needed only on OSes that refuse to let you replace a running binary on disk.

As written, the fslsrv script assumes a Linux environment. It expects /bin/bash to exist, and it depends on non-POSIX tools like pgrep. It should not be difficult to port to systems like macOS or the BSDs.


On Debian and Ubuntu systems the primary user-level configuration file for nginx is /etc/nginx/sites-enabled/default. I recommend that this file contain only a list of include statements, one for each site that server hosts:

  include local/example
  include local/foo

Those files then each define one domain’s configuration. Here, /etc/nginx/local/example contains the configuration for * and *; and local/foo contains the configuration for *

The configuration for our web site, stored in /etc/nginx/sites-enabled/local/foo is:

  server {
      include local/generic;

      access_log /var/log/nginx/;
       error_log /var/log/nginx/;

      # Bypass Fossil for the static Doxygen docs
      location /doc/html {
          root /var/www/;

          location ~* \.(html|ico|css|js|gif|jpg|png)$ {
              expires 7d;
              add_header Vary Accept-Encoding;
              access_log off;

      # Redirect everything else to the Fossil instance
      location / {
          include scgi_params;
          scgi_param HTTPS "on";
          scgi_param SCRIPT_NAME "";

As you can see, this is a simple extension of the basic nginx service configuration for SCGI, showing off a few ideas you might want to try on your own site, such as static asset proxying.

The local/generic file referenced above helps us reduce unnecessary repetition among the multiple sites this configuration hosts:

  root /var/www/$host;

  listen 80;
  listen [::]:80;

  charset utf-8;

There are some configuration directives that nginx refuses to substitute variables into, citing performance considerations, so there is a limit to how much repetition you can squeeze out this way. One such example is the access_log and error_log directives, which follow an obvious pattern from one host to the next. Sadly, you must tolerate some repetition across server { } blocks when setting up multiple domains on a single server.

The configuration for and is similar.

See the nginx docs for more ideas.

Return to the top-level Fossil server article.