Skip to content
Infrastructure

Connecting iPhone and Apple TV to Headscale

By Victor Da Luz
tailscale headscale ios tvos apple-tv networking homelab wireguard

In the previous post on self-hosting Headscale, I covered deploying the coordination server, fixing deprecated config keys, and connecting macOS and Linux clients with pre-auth keys. That part is straightforward: generate a key, run tailscale up --authkey=<key>, done.

iOS and tvOS don’t work that way.

Why pre-auth keys don’t work on iOS

Pre-auth keys are a Tailscale feature. The official iOS client supports them for the hosted Tailscale service, but when you point the client at a custom coordination server, pre-auth key authentication isn’t supported. This is a known limitation in the upstream iOS app, not a Headscale bug.

The workaround is interactive registration: the iOS client generates a registration key, you find it in the server logs, and approve the device via the Headscale CLI. It works, but it’s a few more steps than the one-liner on other platforms.

iOS setup

Open the Tailscale app on your iPhone. Go to Settings (the gear icon at the top right), tap your account name, then Log Out.

From the login screen, tap Log In. You’ll see a field to enter a custom coordination server. Enter your Headscale URL:

https://headscale.example.net

The app loads a page and waits. At this point, the device is trying to register but needs approval. On your Headscale server, check the logs to find the registration key it generated:

sudo docker logs headscale 2>&1 | tail -20

Look for a line like:

INF Starting node registration using key: nodekey:abc123...

Copy that key and register the device:

sudo docker exec headscale headscale nodes register --user homelab --key nodekey:abc123...

The --user flag here takes the username string directly, not a numeric ID. That’s different from preauthkeys create, which uses -u <numeric-ID>.

The iOS app shows “Connected” as soon as the registration goes through.

The localhost hostname problem

After registration, check what name your iPhone registered with:

sudo docker exec headscale headscale nodes list

You’ll see something like:

ID | Hostname  | Name      | Last Seen           | Connected
1  | localhost | localhost | 2025-06-17 10:22:10 | online

The iPhone registers with hostname localhost. That’s technically accurate from the phone’s perspective, but it makes the node list confusing.

Rename it:

sudo docker exec headscale headscale nodes rename --identifier 1 iphone

The Name column is what Headscale and other Tailscale clients see. localhost stays in Hostname (it’s what the device reported), but iphone is what shows up in the node list going forward.

tvOS (Apple TV) setup

Apple TV follows the same interactive registration flow, but the input method is different - you’re working with a TV remote, not a keyboard.

On your Apple TV, open the Tailscale app. Go to SettingsAccountLog In. The app asks for a custom coordination server and then displays a QR code on the screen.

Scan the QR code with your iPhone. It opens a browser to a Headscale registration URL. The Apple TV is now waiting for approval.

Back on the server, check the logs for the registration key:

sudo docker logs headscale 2>&1 | tail -20

Register the device the same way:

sudo docker exec headscale headscale nodes register --user homelab --key nodekey:xyz789...

Give it a useful name:

sudo docker exec headscale headscale nodes rename --identifier 2 appletv

The TV shows “Connected.”

VPN-on-demand and home WiFi

The Tailscale iOS app has a VPN On Demand feature that automatically connects when you leave home. In practice this means: the VPN connects when you’re on cellular or an unknown network, and doesn’t connect when you’re on your home WiFi.

If you look at Headscale’s node list while your iPhone is on home WiFi, it shows the device as offline. That’s expected. The VPN isn’t running - the phone doesn’t need Headscale to reach home resources when it’s already on the home network.

When you leave and connect via cellular, Tailscale connects automatically and the node shows online again.

Comparing platforms

PlatformPre-auth keysMethod
macOSYestailscale up --authkey=<key>
LinuxYestailscale up --authkey=<key>
iOSNoInteractive login → CLI nodes register
tvOSNoQR code → CLI nodes register

The pre-auth key path is fast: generate a key on the server, run one command on the client, done. The interactive path adds a few steps but isn’t complicated once you know what the logs look like.

Lessons

Check the logs immediately. After the iOS app shows its registration screen, run docker logs headscale | tail -20 right away. The node key appears once and doesn’t persist anywhere obvious. If you miss it, log out of the app and log back in to generate a new one.

Rename before you forget. Once localhost is in your node list, it’s easy to lose track of which device it is. Rename immediately after registration.

--user takes a name, not a number. headscale nodes register --user homelab works. Coming from preauthkeys create -u 1 (numeric), I expected the same flag here - it’s not.

The tvOS QR code is the friendliest part. Entering a URL with a TV remote would be painful. The QR code flow is well-designed: scan with phone, approval comes from the server CLI, TV shows connected.

Related reading

Infrastructure

Self-hosting Tailscale with Headscale

Replacing Tailscale's hosted coordination server with Headscale on a Proxmox LXC: Docker Compose setup, deprecated config keys that blocked startup, and connecting macOS, Linux, and iOS clients.

Read

Ready to Transform Your Career?

Let's work together to unlock your potential and achieve your professional goals.