Tailscale is incredible, but sometimes you want full control over your data, your network, and your limits. Enter Headscale: the open-source implementation of the Tailscale coordination server.
While Headscale is powerful, it is CLI-first. To make it user-friendly, we will pair it with Headplane, a modern web UI. We will wrap the UI in Nginx for SSL and configure Google OIDC so you can log in with your existing credentials.
________________________________
To keep things clean, create a folder for your stack. We will map volumes relative to this folder.
Make sure your SSL certificates (fullchain.pem and privkey.pem) are located inside _certs/.
Create _config/config.yaml. Crucial Note: We are running Headscale on port 28011. This is where your VPN clients (Windows, Mac, etc.) will connect. The Web UI will live on standard port 443.
Create _headplane_config/config.yaml. This controls the Web Dashboard.
Create _nginx/nginx.conf. This handles the web traffic for the UI.
Create docker-compose.yaml.
docker compose up -d
docker exec headscale headscale apikeys create --expiration 90d
docker compose restart headplane
You can now visit https://vpn.example.com and log in via Google OIDC!
Here is the critical part. Standard Tailscale clients try to talk to tailscale.com. We need to point them to https://vpn.example.com:28011.
defaults write io.tailscale.ipn.macOS ControlURL https://vpn.example.com:28011
tailscale login --login-server https://vpn.example.com:28011
sudo tailscale up --login-server https://vpn.example.com:28011