Tailscale SSH into a headless Mac
A Mac with no screen attached runs a few scheduled jobs for me. It sits behind an ordinary home router, has no public IP, and I have never opened a port on that router. From my laptop, wherever the laptop happens to be, ssh into it works as if it stood on the desk. Tailscale does the networking, and the part most people skip is that it can do the SSH keys too.
The setup
Install Tailscale on both machines and log in with the same account. Every machine gets an address inside the private network and, with MagicDNS switched on in the admin console, a name that is simply its hostname. On the Mac you want to reach, one flag turns on Tailscale SSH:
tailscale up --ssh
From then on authentication is the Tailscale identity, not an authorized_keys file. A device logged in as me gets in; a device that is not does not reach port 22 at all. A new laptop needs no key copied anywhere, it needs to be logged in. Retiring an old laptop is "remove it from the admin console". The first connection from a new device opens a browser tab asking me to confirm it is really me; that is the check mode of the default policy, and for machines you trust it can be relaxed to accept.
Two macOS details. The App Store app can connect to Tailscale SSH servers but cannot be one; for the server side you want the Homebrew build (brew install tailscale), which runs tailscaled as a service. And I leave Remote Login in System Settings on anyway, with a key, as the fallback for the day Tailscale SSH itself is the thing I am debugging.
Then ssh <account>@<machine> from anywhere with internet, and it is the same shell every time. What else I use Tailscale for is on its tool page.
When it stops answering
It has stopped answering three times. Each time it was one of these, in this order.
The client is not running. After a macOS update the Mac rebooted, came back on the network, and never rejoined Tailscale because the daemon was not registered to start at boot. tailscale status on the laptop showed it offline with a "last seen" hours ago. Fix once: sudo brew services start tailscale writes the launchd job. Then reboot on purpose and check it comes back.
The login expired. Node keys expire after 180 days by default, and a machine without a keyboard has nobody to click "reauthenticate". The admin console shows the device with an expired badge. For headless machines I tick "Disable key expiry" on the day I add them. Do it on day one, because day 180 is always a Sunday.
The Mac is asleep. A Mac with no display still sleeps, and a closed MacBook sleeps harder. Energy settings: prevent sleeping when the display is off, wake for network access. For a closed lid, sudo pmset -a disablesleep 1 does what the settings pane will not. Asleep looks exactly like offline from the laptop, so I check this one before I suspect anything else.
The quick triage
From the laptop, tailscale status tells me whether the Mac is online at all. If it is online and SSH still refuses, the problem is on the Mac side: a later tailscale up without --ssh complains and asks for --reset, and if you obey it the flag is gone (flags are not sticky, repeat all of them). Or the policy changed. If it is offline, it is one of the three above, and the one at the top of the list has been the culprit most often.
