Featured image of post Making systemd Services Wait for Tailscale on NixOS

Making systemd Services Wait for Tailscale on NixOS

On my NixOS servers, I set up services (here, nginx) listening on the tailscale IP. However, during deployment the service sometimes fails to start, despite specifying the service to start once tailscale is online:

systemd.services.nginx = {
    after = [ "tailscaled.service" ];
    requires = [ "tailscaled.service" ];
};

For instance:

déc. 21 14:30:52 garmr nginx-pre-start[347563]: nginx: [emerg] bind() to 100.65.1.5:443 failed (99: Cannot assign requested address)

This suggests the service is considered active by systemd before the interface is ready. A GitHub Issue confirms this behaviour: Tailscaled tells systemd that it is ready before its ip address is bindable #11504, where the comments suggest to use approaches relying on a sleep as a workaround. Alternatively, we can rely on the systemd-networkd-wait-online.service service. By providing an interface name using -i interface_name it will wait for the specified interface to be configured by systemd-networkd and online. This is a oneshot service, meaning that once the main program exits, the service will be considered as active. Hence, it can be used as a dependency for units requiring specific interfaces to be online.

This can be done declaratively using the following configuration for the IPv4 interface named tailscale0 as follows:

  systemd.services.tailscale-online = {
    description = "Wait for Tailscale to have an IPv4 address";

    requires = [ "systemd-networkd.service" ];
    after = [ "systemd-networkd.service" ];
    
    serviceConfig = {
      Type = "oneshot";
      RemainAfterExit = true;

      ExecStart = "${pkgs.systemd}/lib/systemd/systemd-networkd-wait-online -i tailscale0 -4";
    };
  };

It creates a custom service named tailscale-online, started once systemd-networkd is up. The service is declared as a oneshot and RemainAfterExit is set to true, it remains in the “active” state after systemd-networkd-wait-online exits. Now, the nginx service can depend on tailscale-online as follows:

  systemd.services.nginx = {
    after = [ "tailscale-online.service" ];
    requires = [ "tailscale-online.service" ];
  };

Banner image: John Singer Sargent (1878). Capri Girl on a Rooftop. Crystal Bridges Museum of American Art, Bentonville, Arkansas.

Built with Hugo
Theme Stack designed by Jimmy