Part of project: Bosun

Intro to Bosun and Version 0.1

Posted on June 07 2026

I run a little homeserver called grasshopper which is a ThinkCenter M920Q with a RocketStor 8-bay DAS wired up to it. It started out as a media server running Jellyfin, then expanded to also run transmission-daemon as part of SciOp. Now it’s also running my Forgejo instance and an inventory system called Homebox. I also sometimes use it as a VPN gateway when I need to connect into my home network remotely.

With all of these services running, I’d like to setup notifications that are sent out under certain conditions, especially when one of the hard drives in my RAID array fail. I imagine there’s already tools out there that do what I want (you can probably get Prometheus to do this is you know how to set it up) but I reckon I’ll have a go a making it myself. For version 0.1 my objective will just be to render a dashboard web page which shows system packages (with available updates), docker containers (grouped by docker compose projects), MD RAID arrays, and general disk utilisation. No email sending yet, that will come later. All of these features should be implemented by a simple server application that can be easily installed from a DEB package.

System Packages

This wasn’t too bad. I ended up using dpkg-query -- show to get an easy to parse list of all the packages on the system. I added apt-get dist-upgrade -s (the -s is simulate so it doesn’t make changes and can be run without root) to find out which packages could be updated. I did get side tracked with apt list --upgradeable but that’s not supposed to be machine parsed since it’s output is more regularly changed with improvements to the tool, so I had to find an alternative. The only bit I’m not thrilled about is that it takes about a second to run, which will mean the dashboard page will be slow to load since I don’t want any javascript involved in this project and so intend to server-side render everything. Oh well, maybe I’ll come up with something later.

Docker

The docker daemon has this nice API called /containers/json which is ideal for my purposes. It does mean that the user this tool runs as will need to be in the docker group, but I think that’s unavoidable. It’s a shame there doesn’t seem to be a way to restrict access to read-only APIs like /containers/json. I was also looking to add an extra feature where I check the docker remote registries to see if a newer image is available to show that as an available update, but it seemed quite complicated. I was trying to speak the distribution API myself, rather than user docker for it. I got as far as getting manifests for the images (and wrote a WWW-Authenticate header parser along the way), but I’m giving up on that for now.

MD RAID

This was quite the adventure. I ended up going with parsing /proc/mdstat since it doesn’t need root permissions. I couldn’t find any kind of specification for the format so I read the Linux 6.8 source code (that’s the version grasshopper runs) to write a parser against it. I’m really happy with what I came up with which is available as a separate library called zacjw-mdstat. No idea if it’s compatible with different versions, send me an email if you try.

Disk Usage

Compared to the MD RAID section, this was easy. I threw together a quick and dirty parser for df -h in a few minutes. It’s probably not the most robust (I think I’m supposed to use the -P flag for this sort of thing) but it’ll work for now.

Rendering the Dashboard

For server-side rendering I’m using liquid, specifically Cobalt’s liquid-rust implementation since it’s easy to extend if I need to. The only thing of note here is my use of a CSS :has() selector trick to use checkboxes styled as buttons to support expanding a tile on the dashboard to take up the full page without any javascript.

Finishing up

I added a TOML format config file for specifying the address and port to bind, as well as controlling which modules to enable. I added a systemd service unit file which will get installed with the server. I then added the necessary metadata for DEB packaging with kornelski’s cargo-deb. I also setup a CI workflow in Forgejo to handle building the package automatically when I merge into the release branch. Turns out Forgejo supports hosting an APT repo with automatic package signing so I set that up without too much trouble.

You can find the version 0.1 release of Bosun here. Version 0.2 should be ready soon too so expect another writeup here before too long.

Webmentions

Mentioned this post on your website? Enter the link below and I'll add it to this section.