I can't activate a systemd target with a service

I want to implement a systemd user service which determines my network environment. A NetworkManager hook is not sufficient in my case because it has to run in the user space. Therefore I can’t access the network-online.target as well.

It’s planned that other services depend on this. Here’s a quick overview of what I’m trying to do.

image

In my configuration.nix I implemented it like this (the timer isn’t implemented yet):

systemd.user = { 
  targets = { 
    network-online = { 
      description = "Network interfaces are up";
      wants = [ "if-up.service" ];
    };  
  };
  services = {
    netenv = {
      description = "Determine in which network environment the machine is when the network comes up";
      after = [ "network-online.target" ];
      wants = [ "network-online.target" ];
      serviceConfig = {
        Type = "exec";
        Environment= "PATH=/run/current-system/sw/bin:$PATH";
        ExecStart = "%h/bin/netenv.sh";
        SyslogIdentifier="NetEnv";
      };
    };
    if-up = {
      description = "Monitor interface status";
      wantedBy = [ "network-online.target" ];
      before = [ "network-online.target" ];
      serviceConfig = {
        Type = "oneshot";
        Environment= "PATH=/run/current-system/sw/bin:$PATH";
        SyslogIdentifier="Interface-Watch";
        RemainAfterExit="Yes";
      };
      script = "nm-online";
    };
    do-stuff = {
      description = "Do something with the network connected";
      requisite = [ "network-online.target" ];
      serviceConfig = {
        Type = "simple";
        ExecStart = "%h/bin/stuff.sh";
      };
    };
  };
};

In theory, network-online.target should be activated after I run systemctl --user start if-up.service successfully and I think netenv.service should start as well.

But nothing happens after if-up.service completes. What am I missing? Is this even tested/supported in NixOS?

There is no network-online.target in the user session. It’s a system level thing.

Yeah that’s right and that’s the reason why I’m defining it by myself as you can see above :wink:

(caveat: This may be completely wrong, it’s just my understanding which I’m not entirely certain of. For the canonical documentation, have a look at man systemd.unit.)

systemd has two main unit relationship types which you may be mixing up a little here: ordering, and dependency. Ordering (Before= and After=) define when a unit is allowed to start relative to other units, while dependency relationships (including Wants=, WantedBy=, Requires=, …) define which other units are pulled in by one.

Only the dependency relationships are relevant to what you’re expecting here, I believe. (network-online.target wants if-up.service), which means that if you try to start network-online.target, if-up.service will be started as well. The (if-up.service before network-online.target) relationship means that network-online.target will not be reached until if-up.service has started, but not that network-online.target will be started automatically when if-up.service has started.

You may want to either

  • start network-online.target instead of if-up.service, or
  • add wants = ["network-online.target"]; to if-up.service so that starting the service brings the target in as well.

I think this is also incorrect — again, you have no dependency relationship, only ordering. Try adding wantedBy = ["network-online.target"] to netenv.service or adding netenv.service to the wants field of network-online.target.

You’re absolutely right. For some reason I confused the direction of the dependencies. Thanks a lot for pointing that out.