djhworld

thoughts


Socket activated services

I’ve been running Linux on my personal computer for nearly six months now and just thought I’d write about a neat feature in systemd called systemd.socket

Personal finance on-demand

tl;dr Created a socket activated service to spin up a local webapp I use sometimes when something connects to it, and then tear it down again after 5 minutes

I’m a big personal finance nerd and have spent the last 3 years cultivating a ledger file that contains pretty much every facet of my financial life. This file is in a format understood by a suite of command line software called Beancount. It’s is made even better used in conjunction with Fava, a webapp to explore your beancount file.

Fava is a HTTP a service that reads your beancount file stored on disk and presents you with a series of reports and charts to help you make sense of it. I usually only use it when editing my file or occasionally when I need to check something.


Example screenshot taken from https://beancount.github.io/fava/

For quite some time I had this as a --user level systemd service, enabled to run on startup. This worked fine, but I noticed recently that the process seems to consume 2% CPU at all times. Not a big deal in the grand scheme of things but battery life on my laptop comes at a premium.


Fava CPU usage

This made me think, what if there is a way of starting up Fava only when I need it 🤔, sort of like the serverless compute world where resources are spun up on-demand. It was at this point I remembered reading about systemd.socket where you can activate a service when something connects on a socket. At the time I’d filed it away under the “that sounds cool but I’ll probably never use it” place in my brain, but it’s pleasing to now have an actual use case!

The setup is as follows

Create a fava.socket file under ~/.config/systemd/user - this sets up the socket.

[Unit]
Description=Fava Socket
PartOf=fava.service

[Socket]
ListenStream=127.0.0.1:5000

[Install]
WantedBy=sockets.target

Create a fava.service file under ~/.config/systemd/user - this defines the service

[Unit]
Description=Fava
After=network.target fava.socket

[Service]
Type=simple
ExecStart=fava -H 127.0.0.1 /home/djh/beancount/financials.beancount
RuntimeMaxSec=300 # kill the service after 5 minutes.

[Install]
WantedBy=default.target

Enable and start the socket + service

systemctl --user daemon-reload
systemctl --user enable fava.socket
systemctl --user start fava.socket

Go to a web browser and visit http://127.0.0.1:5000 and Fava should load.

After 5 minutes the service will be terminated, but you can start it again by refreshing the page.

Ideally I’d want the socket to terminate the service if nothing connects to it for 5 minutes but I’m not sure how to do that, hence the RuntimeMaxSec setting in the service file 😞

Better ways

You might be wondering why not just host Fava on a Raspberry Pi, it’s a web application afterall. In my case I tend to only use it when editing my beancount file which is stored locally on my Mac. Changes are committed to a git repository when I’m happy with them. The hosted version could listen to changes to this git repository and keep its local copy up to date I suppose, but I’d have to constantly create/push commits to see my changes. This approach also requires network access.

Another way would to just run fava directly on the command line when I need it, but I like the idea of requesting it on demand via a HTTP request and letting systemd handle the rest.

Anyways just thought I’d share x.