Start Python script from systemd-unit?

This is my Gitea backup script configuration:

  systemd.services.gitea-backup = {
    description = "gitea backup";
    path = [ pkgs.gitea ];
    onFailure = [ "gitea-pushover@%n.service" ];
    serviceConfig = {
      Type = "oneshot";
      User = "gitea";
      WorkingDirectory = "${pathGitea}";
    };
    script = "${pkgs.gitea}/bin/gitea dump -c /var/lib/gitea/custom/conf/app.ini -f gitea-dump-$(date +%Y%m%d).zip";
  };
  systemd.services.gitea-pushover = {
    description = "gitea pushover";
    serviceConfig = {
      Type = "oneshot";
      User = "root";
    };
    script = "${pkgs.python3}/bin/python3 /root/bin/script.py";
  };
  environment.systemPackages = with pkgs; [
    (python3.withPackages(ps: with ps; [ requests ]))
    ...
    ...
  ];
systemd[1]: Starting gitea pushover ok...
4c0972k9p4hjp4pmhvrpy3cpfm1rihr6-unit-script-gitea-pushover-ok-start[1024]: Traceback (most recent call last):
4c0972k9p4hjp4pmhvrpy3cpfm1rihr6-unit-script-gitea-pushover-ok-start[1024]:   File "/root/bin/botopensync.py", line 9, in <module>
4c0972k9p4hjp4pmhvrpy3cpfm1rihr6-unit-script-gitea-pushover-ok-start[1024]:     import requests
4c0972k9p4hjp4pmhvrpy3cpfm1rihr6-unit-script-gitea-pushover-ok-start[1024]: ModuleNotFoundError: No module named 'requests'
systemd[1]: gitea-pushover-ok.service: Main process exited, code=exited, status=1/FAILURE
systemd[1]: gitea-pushover-ok.service: Failed with result 'exit-code'.
systemd[1]: Failed to start gitea pushover ok.

How can I add this Python module to my systemd-unit?

1 Like

Systemd services don’t inherit the PATH of the system profile.
Instead, you must explicitly define the dependencies of your service.
Here’s one way to do this:

systemd.services.gitea-pushover = {
  description = "gitea pushover";
  serviceConfig = {
    Type = "oneshot";
    User = "root";
    ExecStart = let
      python = pkgs.python3.withPackages (ps: with ps; [ requests ]);
    in
      "${python.interpreter} /root/bin/script.py";
  };
};

Note that I’ve used ExecStart as we don’t need the Bash wrapper script that’s implied by script. And python.interpreter is just a shorthand for "${python}/bin/python3.x".

Hint: Put /root/bin/script.py in a Nix derivation so that it gets automatically deployed via nixos-rebuild or nixos-install.

4 Likes

I suspected that, but I had no idea where to look it up.

Good input!

Thank you very much!

This may be a noob question, but what does an example of that look like?

The goal is to make script.py part of the system derivation so that it’s guaranteed to be available when the system starts.

E.g., this can be achieved by defining the service like this:
ExecStart = "${python.interpreter} ${./my/local/script.py}";

1 Like