Headscale lets you run your own Tailscale network. Your phones, laptops and servers use the ordinary Tailscale apps, but they sign in to your server instead of Tailscale's, and join one private network wherever they are.
On this page
- At a glance
- What it's good for
- Before you install: give it a domain
- Signing in to Headplane
- Adding your first device
- How the network behaves
- Relays
- Good to know
- Addresses and ports
- Settings
- In its config file
- Tools
- Works with
- Privacy and security
- What leaves this server
- What LibrePortal turns off
- Sources
- Under the hood
Good to know before you install
This is a separate VPN from LibrePortal's built-in private network and is not used for LibrePortal's own access control; turning on Tailscale's public relays lets Tailscale Inc. see which of your devices talk.
At a glance#
- What it is
- Mesh VPN Control Server Networking
- Where it opens
- Headplane (admin UI)
On its own port at
http://your-server:port, oradmin.headscale.your-domainonce you have a domain - Updates
- When you choose New versions wait for you to press Update
- Backups
- Included Part of the daily backup when that's switched on
- Copies
- One per server
What it's good for#
Tailscale is a popular way to put all your devices on one private network, so your laptop can reach your home server, or your parents' computer, as if they were in the same room. The apps are free and very good, but normally they're run by Tailscale's own servers, which keep the list of your devices. Headscale is an open-source replacement for that part. The devices still talk to each other directly and encrypted; Headscale just introduces them.
It suits you if you already like Tailscale, want several machines in different places on one network, or need something the LibrePortal VPN doesn't do, like devices reaching each other rather than just your server.
Headscale or the LibrePortal VPN?
If all you want is to reach this server's apps from your phone when you're out, use the LibrePortal VPN. It's built in, and it's what LibrePortal's own "trusted places" understand. Headscale is a separate network: a device on it is not trusted by LibrePortal, so private apps stay closed to it. Choose Headscale when you want a mesh of your own devices.
Headscale only appears in the App Center in Advanced mode.
Before you install: give it a domain#
The Tailscale apps expect to reach their control server over HTTPS, and so does the relay Headscale runs for devices that can't connect directly. So set up a domain with Traefik first. Headscale then lives at headscale.<your domain> and its admin page at admin.headscale.<your domain>.
Without them, Headscale installs on your server's plain address and port, and the install warns you that the apps need HTTPS. Every device signs in against that address, so moving Headscale afterwards means reinstalling it and signing every device in again. It's much easier to get the address right first.
Signing in to Headplane#
LibrePortal installs Headplane alongside Headscale, a web page for looking after your network. Open it with the Headplane (admin UI) button.
Headplane doesn't have a username and password. It asks for a Headscale API key, and LibrePortal made one for you at install: it's the password in the Login Details shown when the install finished (press Welcome on the app's page to see it again). The key lasts a year. When it runs out, or if you think someone else has seen it, run Create Headplane Login Key on the Tools tab and use the new one.
Headplane is kept to trusted places to start with, while Headscale itself is public, because devices on other networks have to be able to reach it to join.
Adding your first device#
Devices belong to a user, so make one first, then give the device a key.
- Run Create User on the Tools tab, with a short name in lowercase, like
alice. - Run Create Pre-Auth Key for that user. Valid for is how long the key can be used to join (an hour if you leave it empty); tick Reusable if you want to add several devices with the same key. The task prints the key and the exact command to use.
- On a computer with Tailscale installed, run the command it gave you. It looks like this:
tailscale up --login-server https://headscale.example.com --authkey <the key> - Check it joined with List Devices, which shows every device, its address on your network and whether it's online.
On a phone, it's the Tailscale app's own settings. On Android, open the settings, go to Accounts, and use the menu to pick Use an alternate server, enter your Headscale address, then choose Use an auth key and paste the key. On iPhone, tap the account icon, choose Log inβ¦, and pick Use custom coordination server from the options. Headscale's documentation has the steps for each platform.
Once they've joined, every device gets an address starting 100. and a name ending in tailnet.internal, so you can reach your laptop as something like laptop.tailnet.internal.
To check: document signing in without a pre-auth key (the browser login page asks an admin to run headscale nodes register) once LibrePortal has a tool for approving it, or confirm Headplane 0.7 can do it
How the network behaves#
LibrePortal sets Headscale up for a household, not an office:
- Every device can reach every other device. There's no access policy, so nothing is held back between them. Anyone you give a key to joins that same open network, so only hand keys to people you'd let into your house.
- Devices don't expire. They stay signed in until you remove them.
- Looking things up. While connected, devices use Quad9 for everyday DNS, plus the
tailnet.internalnames for each other. That replaces the DNS they'd otherwise use, so a device at home stops using your AdGuard or Pi-hole while Tailscale is on.
Headplane shows Headscale's settings but can't change them: LibrePortal keeps Headscale's settings file read-only to it, so the setup above is what you get unless you edit config/config.yaml in the app folder yourself.
Relays#
Most of the time your devices connect directly to each other. When they can't (both behind strict networks, say), traffic goes through a relay instead. It's still encrypted end to end, so a relay can't read it, but it does see which devices talk.
Relay servers on the Config tab decides which relays are on offer. Self-hosted only (this server) uses the relay Headscale runs itself, so nobody else is involved; that's the default, and it's why UDP port 3478 is open to the internet. Also use Tailscale's public relays adds Tailscale's worldwide ones, which can be faster for devices far from your server, at the cost of Tailscale Inc. seeing which of your devices talk.
Good to know#
- Updates wait for you. LibrePortal pins an exact Headscale version, because Headscale drops settings between versions and has to be upgraded one step at a time. Update only brings rebuilds of that version, with a backup taken first. The Updates tab may offer a newer release, but Upgrade can't move Headscale to it yet.
- Backups include Headscale's database (users, devices and keys), its private keys and its settings. Restoring them brings your network back without re-registering anything.
- One per server. A Tailscale network is defined by its one control server.
- Headscale is not LibrePortal's VPN, and nothing in LibrePortal's access controls knows about it.
Addresses and ports#
Headscale has 3 ports. LibrePortal picks a free port number for each when you install, so yours will differ from anyone else's. The Config tab's Network section shows them, and the app's Open button uses the right one. "Anyone" means it's reachable from wherever your server is reachable; you can make it private later.
| What it is | Who can reach it | With a domain |
|---|---|---|
| Control server (Tailscale clients connect here) TCP | Anyone | headscale.your-domain |
| Headplane (admin UI) | Trusted places only | admin.headscale.your-domain |
| Built-in relay: STUN (NAT traversal) UDP | Anyone | Port only |
Settings#
These are Headscale's own settings on its Config tab. The settings every app shares, like backups, updates and where its data lives, are explained in App pages and settings.
| Setting | What it does | Starts as |
|---|---|---|
| Relay servers | Where devices relay traffic when they can't connect directly. Choices: This server only, Plus Tailscale's |
self |
In its config file#
A few more aren't on the Config tab yet. They live in the app's config file, described here so you know they exist. How to change them.
| Setting | What it does | Starts as |
|---|---|---|
NameCFG_HEADSCALE_DISPLAY_NAME |
What this app is called on your dashboards. Leave empty to use its usual name | Empty |
Show on the LibrePortal DashboardCFG_HEADSCALE_SHOW_ON_DASHBOARD |
Whether this app gets a shortcut on the front page of the LibrePortal WebUI, separate from any Dashy dashboard Choices: true, false |
false |
Detailed logsCFG_HEADSCALE_VERBOSE_LOGS |
Whether Headscale writes its normal, chattier logs, including who connected, instead of only warnings and errors. Choices: true, false |
false |
Debug logging for 24 hoursCFG_HEADSCALE_DEBUG_LOGS |
Turn on to see everything Headscale is doing while you look for a problem; it turns itself off after 24 hours. Choices: true, false |
false |
Tools#
Headscale's Tools tab has 5 buttons for jobs you'd otherwise need a terminal for. Each one runs as a task, so you can watch it on the Tasks page and read what it did. More about Tools.
Add a Headscale user. Devices are registered under a user.
Make a key that registers a device for a user without logging in (tailscale up --login-server <url> --authkey <key>).
Show every Headscale user.
Show every registered device (node), its address and whether it is online.
Make a new Headscale API key for logging in to Headplane (valid for a year). Replaces the one shown under Logins.
Works with#
These are the other apps Headscale knows how to work with. Once it's installed, its page shows the same list with whether each one is connected. How this works.
- Grafana: Shows dashboards for the apps that have one. works with every app
- Prometheus: Collects metrics from the apps that can export them. works with every app
- Uptime Kuma: Watches your apps and shows when one goes down. works with every app
Privacy and security#
All clear Headscale runs your own private VPN control server. LibrePortal turns off its update check and does not use Tailscale's relay servers, so nothing is sent to outside companies.
What leaves this server#
- Your VPN clients (Tailscale apps) have their own update and logging behaviour; Tailscale documents how to opt out
- DNS pushed to VPN clients goes to Quad9
What LibrePortal turns off#
- Detailed logs and per-request access logging (switchable in Config)
- Update check on startup
- Tailscale's relay map (only your own relay is used)
Licence: BSD-3-Clause (Headscale); MIT (Headplane). Audited: 21 October 2026. See the privacy of every app and the Promise.
Sources#
Under the hood#
- It runs these images:
headscale/headscale:v0.29.3@sha256:0e7f1c6e4ce6c2a2a001103ecd3fa645a045adf30ac8a5234fe037b43000cd72,ghcr.io/tale/headplane:0.7.1@sha256:394483fd2336ce6b15df360c3e118ddfd45eb8140fe21df9f2611077f5d79d22. The version is pinned, and moves only when you update it. - It only shows in the App Center when the WebUI is in Advanced mode.
- Its own project and documentation: github.com/juanfont/headscale
Ports, settings, tools and connections on this page are read from LibrePortal v0.10.97's app catalogue each time the guide is built.