Reorder of launching Systemd services

OS: Debian 11 Bullseye


  • The Zerotier application adds the zerotier-one.service system service and creates a virtual network interface (when it works).
  • The sshd server default listens to all addresses

Until then, everything is fine with me

Now I am introducing custom config in /etc/ssh/sshd_config.d/my-sshd.conf add ListenAddress that my sshd server accepts calls only at the Zerotier interface address.

Now I suspect that sshd.service starts before zerotier-one.service because after restarting the computer:

$ sudo systemctl status sshd.service
● ssh.service - OpenBSD Secure Shell server
     Loaded: loaded (/lib/systemd/system/ssh.service; enabled; vendor preset: enabled)
     Active: failed (Result: exit-code) since Thu 2023-09-14 17:21:27 CEST; 28s ago
       Docs: man:sshd(8)
    Process: 524 ExecStartPre=/usr/sbin/sshd -t (code=exited, status=0/SUCCESS)
    Process: 551 ExecStart=/usr/sbin/sshd -D $SSHD_OPTS (code=exited, status=255/EXCEPTION)
   Main PID: 551 (code=exited, status=255/EXCEPTION)
        CPU: 21ms

systemd[1]: Starting OpenBSD Secure Shell server...
sshd[551]: error: Bind to port 22 on failed: Cannot assign requested address.
sshd[551]: fatal: Cannot bind any address.
systemd[1]: ssh.service: Main process exited, code=exited, status=255/EXCEPTION
systemd[1]: ssh.service: Failed with result 'exit-code'.
systemd[1]: Failed to start OpenBSD Secure Shell server

So I added the After= option to /etc/systemd/system/ssh.service.d/override.conf changing using the command sudo systemctl edit sshd.service:

[Unit] auditd.service


[Unit] auditd.service zerotier-one.service

It looks like this now:

$ sudo systemctl cat sshd.service
# /lib/systemd/system/ssh.service
Description=OpenBSD Secure Shell server
Documentation=man:sshd(8) man:sshd_config(5) auditd.service

ExecStartPre=/usr/sbin/sshd -t
ExecStart=/usr/sbin/sshd -D $SSHD_OPTS
ExecReload=/usr/sbin/sshd -t
ExecReload=/bin/kill -HUP $MAINPID


# /etc/systemd/system/ssh.service.d/override.conf
[Unit] auditd.service zerotier-one.service

But after restarting the computer, the error still occurs

When I do a sudo systemctl restart sshd.service now I get:

$ sudo systemctl status sshd.service
● ssh.service - OpenBSD Secure Shell server
     Loaded: loaded (/lib/systemd/system/ssh.service; enabled; vendor preset: enabled)
    Drop-In: /etc/systemd/system/ssh.service.d
     Active: active (running) since Thu 2023-09-14 17:40:43 CEST; 2s ago
       Docs: man:sshd(8)
    Process: 3065 ExecStartPre=/usr/sbin/sshd -t (code=exited, status=0/SUCCESS)
   Main PID: 3066 (sshd)
      Tasks: 1 (limit: 9423)
     Memory: 1.0M
        CPU: 21ms
     CGroup: /system.slice/ssh.service
             └─3066 sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups

systemd[1]: Starting OpenBSD Secure Shell server...
sshd[3066]: Server listening on port 22.
systemd[1]: Started OpenBSD Secure Shell server.

I have the impression that the sshd.service is still starting before zerotier-one.service

Is something missing or can it be checked differently?

Should I do something else in addition to adding zerotier-one.service to After=?

EDIT (Information for other users):

In addition to the solution proposed by @telkoM (for which I thank you), another trick solved the problem in my case:

Just add the directive ExecStartPost=sleep 10 to zerotier-one.service or ExecStartPre=sleep 10 to sshd.service

Asked By: DarekH


With your configuration, sshd.service will certainly start only after zerotier-one.service starts. But that is not enough. The sshd.service would need to wait until Zerotier has actually connected successfully, which can happen quite a bit later (in computer timescales, at least). And the current zerotier-one.service is not even trying to provide that information to systemd:

Description=ZeroTier One



You would probably have to create a Type=oneshot service (it could be called zerotier-wait-online.service) that would run a script that includes a loop that calls e.g. zerotier-cli listnetworks or just ip addr show and looks for the IP address If it is not available, the script would sleep a few seconds and try again.

When the script would see the address has appeared, the script would exit – and that would tell systemd that any service configured to run After=zerotier-wait-online.service can now proceed. (Unlike the default Type=simple and several other service types, services of Type=oneshot are only considered "started" after their main ExecStart process has successfully exited – and that’s exactly what you need.

Once you have that service working, you can change your sshd.service override to After=zerotier-wait-online.service, and then it should work as you wanted.

Note that you cannot simply require that zerotier-wait-online.service runs, because zerotier-one.service itself runs Trying to set up such a requirement would create an impossible situation.

The root of the problem is that the use of ListenAddress brings with it the requirement that the specified address must already be up when sshd starts.

If you need sshd to listen in the Zerotier IP address only, but don’t specifically have to use ListenAddress to implement it, you could use alternative ways to implement the restriction.

In /etc/ssh/sshd_config, you could add a Match block like this, to deny access on any local IP address except the Zerotier one:

Match LocalAddress *,!
    DenyUsers *

Or you could use iptables to drop/reject incoming connections if the destination address is anything except

iptables -I INPUT 1 -p tcp --dport 22 ! -d -j DROP

DROP makes blocked connection attempts hang until they time out; if you want the blocked connections to fail quickly, use a rule like this instead:

iptables -I INPUT 1 -p tcp --dport 22 ! -d -j REJECT --reject-with tcp-reset

If you use ufw or some other firewall management system, there is probably a way to configure an equivalent rule to it.

Answered By: telcoM

Any workaround will keep breaking because this is the incorrect layer to solve the problem. It is not on systemd or zerotier.. it is sshd that needs to allow setting the IP_FREEBIND socket option to allow it to listen on addresses that are "not yet" or "ever" configured.

here is the patch to do it , it is correct but unfortunately openSSH developers did not accept it.

Answered By: Cristian Rodríguez