Title: How to account systemd services bandwidth usage on NixOS | |
Author: Solène | |
Date: 20 July 2022 | |
Tags: nixos bandwidth monitoring | |
Description: This article shows how to easily record the bandwidth | |
usage of specific systemd services on NixOS, but this can be applied to | |
Linux in general | |
# Introduction | |
Did you ever wonder how many bytes a system service is daily receiving | |
from the network? Thanks to systemd, we can easily account this. | |
This guide targets NixOS, but the idea could be applied on any Linux | |
system using systemd. | |
NixOS project website | |
In this article, we will focus on the nix-daemon service. | |
# Setup | |
We will enable the attribute IPAccounting on the systemd service | |
nix-daemon, this will make systemd to account bytes and packets that | |
received and sent by the service. However, when the service is | |
stopped, the counters are reset to zero and the information logged into | |
the systemd journal. | |
In order to efficiently gather the network information over time into a | |
database, we will run a script just before the service stops using the | |
preStop service hook. | |
The script checks the existence of a sqlite database | |
/var/lib/service-accounting/nix-daemon.sqlite, creates it if required, | |
and then inserts the received bytes information of the nix-daemon | |
service about to stop. The script uses the service attribute | |
InvocationID and the current day to ensure that a tuple won't be | |
recorded more than once, because if we restart the service multiple | |
times a day, we need to distinguish all the nix-daemon instances. | |
Here is the code snippet to add to your `/etc/nixos/configuration.nix` | |
file before running `nixos-rebuild test` to apply the changes. | |
```nix | |
systemd.services.nix-daemon = { | |
serviceConfig.IPAccounting = "true"; | |
path = with pkgs; [ sqlite busybox systemd ]; | |
preStop = '' | |
#!/bin/sh | |
SERVICE="nix-daemon" | |
DEST="/var/lib/service-accounting" | |
DATABASE="$DEST/$SERVICE.sqlite" | |
mkdir -p "$DEST" | |
# check if database exists | |
if ! dd if="$DATABASE" count=15 bs=1 2>/dev/null | grep -Ea "^SQLite format.[0-… | |
then | |
cat <<EOF | sqlite3 "$DATABASE" | |
CREATE TABLE IF NOT EXISTS accounting ( | |
id TEXT PRIMARY KEY, | |
bytes INTEGER NOT NULL, | |
day DATE NOT NULL | |
); | |
EOF | |
fi | |
BYTES="$(systemctl show "$SERVICE.service" -P IPIngressBytes | grep -oE "^[0-9]… | |
INSTANCE="'$(systemctl show "$SERVICE.service" -P InvocationID | grep -oE "^[a-… | |
cat <<EOF | sqlite3 "$DATABASE" | |
INSERT OR REPLACE INTO accounting (id, bytes, day) VALUES ($INSTANCE, $BYTES, d… | |
EOF | |
''; | |
}; | |
``` | |
If you want to apply this to another service, the script has a single | |
variable SERVICE that has to be updated. | |
# Display the information from the database | |
You can use the following command to display the bandwidth usage of the | |
nix-daemon service with a day-by-date report: | |
```shell | |
$ echo "SELECT day, sum(bytes)/1024/1024 AS Megabytes FROM accounting group by … | |
day Megabytes | |
---------- --------- | |
2022-07-17 173 | |
2022-07-19 3018 | |
2022-07-20 84 | |
``` | |
Please note this command requires the sqlite package to be installed in | |
your environment. | |
# Enhancement | |
I have some ideas to improve the setup: | |
* The script could be improved to support multiple services within the | |
database by using a new field | |
* The command to display data could be improved and turned into a | |
system package to make it easier to use | |
* Provide an SQL query for monthly summary | |
# Conclusion | |
Systemd services are very flexible and powerful thanks to the hooks | |
provided to run script at the right time. While I was interested into | |
network usage accounting, it's also possible to achieve a similar | |
result with CPU usage and I/O accesses. |