2026-06-21 SSH to Atlas over Tailscale

What I set out to do

Be able to SSH into atlas (my Mac) from my phone when off the home network, specifically from the Termius iOS client. Direct sibling to the 2026-06-21 Tailscale Serve for Open WebUI work, and the same governing question: what’s the right transport for reaching my own machine remotely.

What I actually did

Worked it through with Claude reading the prior Tailscale/Cloudflare journal notes for context.

Transport decision: Tailscale, reusing the established split. The 2026-06-21 Tailscale Serve for Open WebUI entry already settled the principle — Tailscale for private, Cloudflare Tunnel for public. SSH to atlas is the textbook private, single-user case: nothing external needs to dial in, so no public endpoint should exist. Cloudflare would mean minting a public hostname for something only I should ever reach — strictly worse here. atlas and the iPhone are already enrolled on the tailnet (tail0cef90.ts.net), so the path was already built.

Confirmed the SSH server was already up — no setup needed on atlas. Couldn’t run systemsetup -getremotelogin (needs admin), so checked without privileges: launchctl print-disabled system showed com.openssh.sshd => enabled, and netstat -an -p tcp showed a listener on *.22 (IPv4 + IPv6). macOS Remote Login was already on; Apple’s sshd was already listening.

Pulled the exact connection values: username achhina, tailnet IP 100.118.183.108, MagicDNS name atlas.tail0cef90.ts.net.

Configured Termius and connected. Host address = the MagicDNS name (a named entry, not the raw IP), port 22. Used username/password auth against my known macOS account rather than setting up a key — simplest path, and WireGuard already encrypts the wire. Connected successfully over the tailnet.

What was striking

  • Nothing to install or enable on atlas. Remote Login was already on, so the whole task was network-path + client config. The transport was a solved problem the moment both nodes were on the tailnet.
  • Termius has no idea Tailscale exists. It’s a vanilla SSH client opening a TCP connection; Tailscale’s iOS Network Extension silently routes it over WireGuard. The only Tailscale-specific requirement is that the iOS toggle be on before opening the session — otherwise atlas has no reachable address and it just times out.
  • A named entry works because MagicDNS resolves app-wide on iOS. The full atlas.tail0cef90.ts.net resolves through the Tailscale extension whenever it’s connected. The bare short name atlas is unreliable in a third-party app (depends on a DNS search domain Termius won’t pick up); full FQDN or the 100.x IP are the reliable forms.
  • sshd binds *.22 (all interfaces), unlike the loopback Serve model. macOS Remote Login listens on every interface, so atlas’s SSH is also reachable from the LAN (not the public internet — no router port is forwarded). Tailscale doesn’t restrict that bind. Acceptable, but it’s a looser posture than the OWUI 127.0.0.1 + Serve-proxy setup, where nothing but the tailnet can reach the socket.

Top 3 next

  1. Optionally swap password → key auth (generate/import a key in Termius, append the public half to atlas ~/.ssh/authorized_keys), then disable SSH password auth.
  2. Optionally lock SSH to tailnet-only to match the loopback philosophy: tailscale up --ssh + disable macOS Remote Login (keyless, ACL-gated, kills the *.22 LAN exposure) — verify the Homebrew build honors the SSH server on macOS first.
  3. Consider mosh (brew install mosh) for resilience to phone sleep / IP changes; Tailscale carries its UDP fine.