Fossil: Serving via systemd on Debian and Ubuntu

Fossil SCM

systemd is the default service management framework on Debian since version 8 and Ubuntu since version 15.04, both released in April 2015.

There are multiple ways to get a service to launch under systemd. We’re going to show two methods which correspond approximately to two of our generic Fossil server setup methods, the inetd and standalone HTTP server methods.

User Service

A fun thing you can easily do with systemd that you can’t directly do with older technologies like inetd and xinetd is to set a server up as a “user” service.

You can’t listen on TCP port 80 with this method due to security restrictions on TCP ports in every OS where systemd runs, but you can create a listener socket on a high-numbered (≥ 1024) TCP port, suitable for sharing a Fossil repo to a workgroup on a private LAN.

To do this, write the following in ~/.local/share/systemd/user/fossil.service:

    [Unit]
    Description=Fossil user server
    After=network-online.target

    [Service]
    WorkingDirectory=/home/fossil/museum
    ExecStart=/home/fossil/bin/fossil server --port 9000 repo.fossil
    Restart=always
    RestartSec=3

    [Install]
    WantedBy=multi-user.target

Unlike with inetd and xinetd, we don’t need to tell systemd which user and group to run this service as, because we’ve installed it under the account we’re logged into, which systemd will use as the service’s owner.

We’ve told systemd that we want automatic service restarts with back-off logic, making this much more robust than the by-hand launches of fossil in the platform-independent Fossil server instructions. The service will stay up until we explicitly tell it to shut down.

A simple and useful modification to the above scheme is to add the --scgi and --localhost flags to the ExecStart line to replace the use of fslsrv in the generic SCGI instructions, giving a much more robust configuration.

Because we’ve set this up as a user service, the commands you give to manipulate the service vary somewhat from the sort you’re more likely to find online:

    $ systemctl --user daemon-reload
    $ systemctl --user enable fossil
    $ systemctl --user start fossil
    $ systemctl --user status fossil -l
    $ systemctl --user stop fossil

That is, we don’t need to talk to systemd with sudo privileges, but we do need to tell it to look at the user configuration rather than the system-level configuration.

This scheme isolates the permissions needed by the Fossil server, which reduces the amount of damage it can do if there is ever a remotely-triggerable security flaw found in Fossil.

On some systemd based OSes, user services only run while that user is logged in interactively. This is common on systems aiming to provide desktop environments, where this is the behavior you often want. To allow background services to continue to run after logout, say:

   $ sudo loginctl enable-linger $USER

You can paste the command just like that into your terminal, since $USER will expand to your login name.

System Service Alternative

There are a couple of common reasons that you’d have cause to install Fossil as a system-level service rather than the prior user-level one:

  • You need Fossil to listen on a TCP port under 1024, such as because you’re running it on a private LAN, and the server has no other HTTP service, so you want Fossil to handle the web traffic directly.

  • You’re proxying Fossil with a system-level service such as nginx, so you need to put Fossil into the system-level service dependency chain to make sure things start up and shut down in the proper order.

There are just a small set of changes required:

  1. Install the unit file to one of the persistent system-level unit file directories. Typically, these are:

    /etc/systemd/system
    /lib/systemd/system
    
  2. Add User and Group directives to the [Service] section so Fossil runs as a normal user, preferably one with access only to the Fossil repo files, rather than running as root.

Socket Activation

Another useful method to serve a Fossil repo via systemd is via a socket listener, which systemd calls “socket activation.” It’s more complicated, but it has some nice properties. It is the feature that allows systemd to replace inetd, xinetd, Upstart, and several other competing technologies.

We first need to define the privileged socket listener by writing /etc/systemd/system/fossil.socket:

    [Unit]
    Description=Fossil socket

    [Socket]
    Accept=yes
    ListenStream=80
    NoDelay=true

    [Install]
    WantedBy=sockets.target

Note the change of configuration directory from the ~/.local directory to the system level. We need to start this socket listener at the root level because of the low-numbered TCP port restriction we brought up above.

This configuration says more or less the same thing as the socket part of an inted entry exemplified elsewhere in this documentation.

Next, create the service definition file in that same directory as fossil@.service:

    [Unit]
    Description=Fossil socket server
    After=network-online.target

    [Service]
    WorkingDirectory=/home/fossil/museum
    ExecStart=/home/fossil/bin/fossil http repo.fossil
    StandardInput=socket

    [Install]
    WantedBy=multi-user.target

We’ll explain the “@” in the file name below.

Notice that we haven’t told systemd which user and group to run Fossil under. Since this is a system-level service definition, that means it will run as root, which then causes Fossil to automatically drop into a chroot(2) jail rooted at the WorkingDirectory we’ve configured above, shortly each fossil http call starts.

The Restart* directives we had in the user service configuration above are unnecessary for this method, since Fossil isn’t supposed to remain running under it. Each HTTP hit starts one Fossil instance, which handles that single client’s request and then immediately shuts down.

Next, you need to tell systemd to reload its system-level configuration files and enable the listening socket:

    $ sudo systemctl daemon-reload
    $ sudo systemctl enable fossil.socket

And now you can manipulate the socket listener:

    $ sudo systemctl start fossil.socket
    $ sudo systemctl status -l fossil.socket
    $ sudo systemctl stop fossil.socket

Notice that we’re working with the socket, not the service. The fact that we’ve given them the same base name and marked the service as an instantiated service with the “@” notation allows systemd to automatically start an instance of the service each time a hit comes in on the socket that systemd is monitoring on Fossil’s behalf. To see this service instantiation at work, visit a long-running Fossil page (e.g. /tarball) and then give a command like this:

    $ sudo systemctl --full | grep fossil

This will show information about the fossil socket and service instances, which should show your /tarball hit handler, if it’s still running:

    fossil@20-127.0.0.1:80-127.0.0.1:38304.service

You can feed that service instance description to a systemctl kill command to stop that single instance without restarting the whole fossil service, for example.

In all of this, realize that we’re able to manipulate a single socket listener or single service instance at a time, rather than reload the whole externally-facing network configuration as with the far more primitive inetd service.

Return to the top-level Fossil server article.