Deploying a Python systemd Service on NixOS

This is a quick note on a mechanism I came up with to declaratively deploy a python systemd service and its dependencies on NixOS.

I wrote a quick service to serve kea leases as json over http. This is for future work in hardware discovery and provisioning.

I wanted this to run in systemd, using only configuration.nix to deploy it and its python packages (flask).

I came up with the following:

A unit to clone or pull the repo on boot:

  systemd.services = {
    clone-kea-lease-server = {
      after = [ "network-online.target" ];
      wants = [ "network-online.target" ];
      wantedBy = [ "kea-lease-server.service" ];
      serviceConfig.Type = "simple";
      script = ''
        if [ ! -d /var/run/kea-lease-server ]; then
          ${pkgs.git}/bin/git clone https://github.com/nihr43/kea-lease-server.git /var/run/kea-lease-server
        else
          ${pkgs.git}/bin/git -C /var/run/kea-lease-server pull
        fi
      '';
    };
  };

and a unit that is the service itself:

  systemd.services = {
    kea-lease-server = {
      after = [ "clone-kea-lease-server.service" ];
      wants = [ "clone-kea-lease-server.service" ];
      wantedBy = [ "default.target" ];
      serviceConfig.Type = "simple";
      environment = { NIX_PATH = "nixpkgs=/nix/var/nix/profiles/per-user/root/channels/nixos"; };
      script = ''
        cd /var/run/kea-lease-server/
        ${pkgs.nix}/bin/nix-shell . --run 'python main.py --host 172.30.190.1'
      '';
    };
  };

Since we’re using nix-shell, dependencies in the project’s default.nix automatically populate at runtime. They’ll be cache in the nix store as well, so this isn’t too terribly wasteful.


Here it all is running:

systemctl status clone-kea-lease-server.service
○ clone-kea-lease-server.service
     Loaded: loaded (/etc/systemd/system/clone-kea-lease-server.service; enabled; preset: ignored)
     Active: inactive (dead) since Tue 2024-12-17 23:45:59 UTC; 9min ago
   Duration: 10.351s
 Invocation: 3eb59b246bde448bbd3f5ced9d728155
    Process: 39491 ExecStart=/nix/store/c73djiiml5inyyzb7gh12bpmn0ffq6qv-unit-script-clone-kea-lease-server-start/bin/clone-kea-lease-server-start (code=exited, status=0/SUCCESS)
   Main PID: 39491 (code=exited, status=0/SUCCESS)
         IP: 6.3K in, 2.6K out
         IO: 0B read, 0B written
   Mem peak: 5.8M
        CPU: 140ms
systemctl status kea-lease-server.service
● kea-lease-server.service
     Loaded: loaded (/etc/systemd/system/kea-lease-server.service; enabled; preset: ignored)
     Active: active (running) since Tue 2024-12-17 23:45:48 UTC; 10min ago
 Invocation: bc8350a46df94d2fa6a89466db83232d
   Main PID: 39492 (kea-lease-serve)
         IP: 27.8K in, 24.5K out
         IO: 0B read, 232K written
      Tasks: 5 (limit: 19129)
     Memory: 45.6M (peak: 286.4M)
        CPU: 13.571s
     CGroup: /system.slice/kea-lease-server.service
             ├─39492 /nix/store/p6k7xp1lsfmbdd731mlglrdj2d66mr82-bash-5.2p37/bin/bash /nix/store/1w8ddaz1ax15241j9a7nwkcib1c6q4bf-unit-script-kea-lease-server-start/bin/kea-lease-server-start
             ├─39493 bash /tmp/nix-shell-39493-0/rc
             ├─39519 python main.py --host 172.30.190.1
             └─39520 /nix/store/zv1kaq7f1q20x62kbjv6pfjygw5jmwl6-python3-3.12.7/bin/python main.py --host 172.30.190.1

Yeah its all running as root. I’ll get around to changing it to the kea user at some point.

Seperating this into two units allows me to restart the service without messing with github, or to trigger just a new clone by restarting the clone- service.

Nathan Hensel

on caving, mountaineering, networking, computing, electronics


2024-12-17