Headscale Quick Start

Goal: reach Jellyfin (and other containers) on your home LAN from anywhere, without exposing anything to the internet. Zero to working in about 15 minutes.

The setup

[Phone / Laptop]
        |
   Tailscale client
        |
[Dedicated Server]   ← public IP, runs Headscale (control plane only)
        |
   Tailscale network (WireGuard mesh)
        |
[LAN Node]           ← Docker host at 192.168.x.x, runs Jellyfin

The dedicated server only coordinates the mesh. Your media streams directly between your device and the LAN node over encrypted WireGuard.

Checklist

  1. ☐ Install Headscale on the dedicated server
  2. ☐ Create a user
  3. ☐ Generate an auth key
  4. ☐ Join the LAN node (and your phone/laptop)
  5. ☐ Open Jellyfin at http://jellyfin:8096 or the Tailscale IP

1. Install Headscale (dedicated server)

From this repo's headscale_dev/ directory:

mkdir -p /data/headscale/config
cp config.yaml /data/headscale/config/config.yaml
nano /data/headscale/config/config.yaml    # set server_url: https://headscale.example.com (YOUR domain)

cp sample.env .env
docker compose up -d

Point your reverse proxy (Nginx Proxy Manager) at container headscale, port 8080, with SSL and websockets enabled. Then verify:

curl https://headscale.example.com/health

2. Create a user

docker compose exec headscale headscale users create homelab

3. Generate an auth key

docker compose exec headscale headscale preauthkeys create --user homelab --expiration 1h

Copy the key it prints.

4. Join the LAN node

On the LAN node (your Docker host running Jellyfin):

curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up --login-server=https://headscale.example.com --authkey <YOUR-PREAUTH-KEY>

On your phone: install the Tailscale app, choose custom / alternate coordination server, and enter https://headscale.example.com. On a laptop, run the same tailscale up command with a fresh key.

Verify from your laptop:

tailscale status
tailscale ping <lan-node-name>

5. Open Jellyfin

With Tailscale connected on your device, open:

Optional — reach the rest of your LAN: on the LAN node run sudo tailscale up --login-server=https://headscale.example.com --advertise-routes=192.168.1.0/24, enable IP forwarding, then approve the route with headscale nodes approve-routes on the server. See the README for details.
Firewall: only 80/443 open on the dedicated server. Never forward port 8096 (Jellyfin) or 8080 (Headscale) on your router.
Lost a device? Cut it off instantly: docker compose exec headscale headscale nodes delete -i <NODE-ID>

Full walkthrough, subnet routing, and gotchas: see README.md in this directory.