Problems setting up systemd timer

I’m trying to setup a systemd timer to run a command daily. I followed the example in the wiki but the service keeps failing when I try systemctl start epg.

  systemd.timers."epg" = {
    wantedBy = [ "timers.target" ];
    timerConfig = {
      OnCalendar = "daily";
      Persistent = true;
    };
  };

  systemd.services."epg" = {
    script = ''
      ${pkgs.nodejs_18}/bin/npm run --prefix /media/TV/epg/ grab -- --site=web.magentatv.de
    '';
    path = [ pkgs.nodejs_18 ];
    serviceConfig = {
      Type = "oneshot";
      User = "root";
    };
  };

The command runs perfectly and also the script produced by NixOS works as intended.

systemctl status epg:

× epg.service
     Loaded: loaded (/etc/systemd/system/epg.service; linked; preset: enabled)
     Active: failed (Result: exit-code) since Tue 2024-04-30 16:46:51 CEST; 12min ago
TriggeredBy: ● epg.timer
    Process: 8145 ExecStart=/nix/store/3h6aq4h1s38z0rhv5irk32j6qx32w2fq-unit-script-epg-start/bin/epg-start (code=exited, sta>
   Main PID: 8145 (code=exited, status=254)
         IP: 0B in, 0B out
        CPU: 401ms

Apr 30 16:46:51 Jans-Server epg-start[8146]: npm ERR! syscall spawn sh
Apr 30 16:46:51 Jans-Server epg-start[8146]: npm ERR! path /media/TV/epg
Apr 30 16:46:51 Jans-Server epg-start[8146]: npm ERR! errno -2
Apr 30 16:46:51 Jans-Server epg-start[8146]: npm ERR! enoent spawn sh ENOENT
Apr 30 16:46:51 Jans-Server epg-start[8146]: npm ERR! enoent This is related to npm not being able to find a file.
Apr 30 16:46:51 Jans-Server epg-start[8146]: npm ERR! enoent
Apr 30 16:46:51 Jans-Server epg-start[8146]: npm ERR! A complete log of this run can be found in: /root/.npm/_logs/2024-04-30>
Apr 30 16:46:51 Jans-Server systemd[1]: epg.service: Main process exited, code=exited, status=254/n/a
Apr 30 16:46:51 Jans-Server systemd[1]: epg.service: Failed with result 'exit-code'.
Apr 30 16:46:51 Jans-Server systemd[1]: Failed to start epg.service.
npm ERR! enoent spawn sh ENOENT

I think you’ll need to add pkgs.bash to path in the service.

Amazing, thank you very much!

Note that you can also use startAt to create the timer:

systemd.services.epg = {
  startAt = "daily";
  # …
};

systemd.timers.epg.timerConfig.Persistent = true;
systemd.services.epg = {
  script = ''
    ${pkgs.nodejs_18}/bin/npm run --prefix /media/TV/epg/ grab -- --site=web.magentatv.de
  '';
  path = [ 
    pkgs.nodejs_18
    pkgs.bash 
  ];
  serviceConfig = {
    Type = "oneshot";
    User = "root";
  };
  startAt = "daily";
};

systemd.timers.epg = {
  wantedBy = [ "timers.target" ];
  timerConfig.Persistent = true;
};

Like this? Do I need wantedBy = [ "timers.target" ];?

And what about the service config?

When you use startAt

systemd.services.epg = {
  script = /* … */;
  path = /* … */;
  serviceConfig = /* … */;
  startAt = "daily";
};

—this timer is created automatically:

# You don’t need to add this.
systemd.timers.epg = {
  wantedBy = [ "timers.target" ];
  timerConfig = {
    OnCalendar = "daily";
  };
};

What’s nice about this is that in many situations you might not need to add systemd.timers.epg at all. In this case it doesn’t have everything you need, but you can add just the missing part:

systemd.timers.epg = {
  timerConfig = {
    Persistent = true;
  };
};

Either way of doing this is fine; it’s just a matter of personal preference.

1 Like

Thank you very much!