What is Headscale?

Headscale is a self-hosted implementation of the coordination service used by Tailscale clients. It helps registered devices discover peers and receive network configuration. You operate that service yourself rather than using Tailscale’s hosted coordination service.

The clients carry application traffic through encrypted connections. Headscale is not simply a central VPN appliance through which every file transfer must pass. Direct connections are preferred where possible, and relays can be used when a direct path cannot be established. Coordination and relay services have different jobs, even when someone hosts both on one machine.

Self-hosting gives you responsibility for coordination metadata, configuration and availability. It does not mean every client, relay, DNS resolver or external service is under your control, and it is not a guarantee of complete privacy or unlimited practical capacity.

Choose a reachable host

The project requirements recommend a suitable Linux/BSD server with a public address and HTTPS on port 443. A home server can be appropriate if the required endpoint is reliably reachable from outside. A home connection behind CGNAT may need a different hosting arrangement; a private LAN address alone is not enough for travelling clients.

Decide who will maintain the server, certificate, domain, updates and backups. Hosting the coordinator at home means household power or internet outages affect that service. A remote server changes that dependency but introduces hosting costs and another machine to secure.

Keep management, metrics and debug services separate from the public client endpoint. Port 9090 is a metrics/debug listener and should not be published openly. Additional ports depend on features such as an embedded DERP server; do not open every port listed in a generic example.

Choose a supported installation method

The official Debian/Ubuntu package instructions are the project’s recommended route on supported distributions. Packages provide the service user, configuration and systemd integration. Choose the release and architecture that match the host and follow that method consistently.

Containers are another option. The project’s container guide is community documentation and explicitly warns that it may be incomplete. The example below is a fresh-install starting point behind an already configured HTTPS reverse proxy, not a complete internet-facing stack.

Prepare a version-matched configuration

This example pins release 0.29.4. For a new empty project directory, create separate configuration and persistent-data folders and download the example from that exact release tag:

mkdir -p headscale/config headscale/lib
cd headscale
curl --fail --location \
  https://raw.githubusercontent.com/juanfont/headscale/v0.29.4/config-example.yaml \
  --output config/config.yaml

Do not overwrite an existing installation’s configuration with this fresh-install download. The configuration guide says to use the same tag as the deployed version; a development-branch example may contain unreleased settings.

Edit the existing keys in that full file. For the reverse-proxy arrangement below, set your real HTTPS server URL and make the application listener reachable inside the container. The following shows fields to edit, not a complete replacement configuration:

server_url: https://headscale.example.com
listen_addr: 0.0.0.0:8080
metrics_listen_addr: 127.0.0.1:9090

Replace the example hostname with the domain you control. Keep the template’s persistent paths under /var/lib/headscale, and review its DNS, policy and relay settings. Do not define the same YAML key twice. If the reverse proxy terminates TLS, use the matching TLS configuration and protocol handling described in the proxy documentation.

Private container starting point

Save this as compose.yaml beside the two folders. The application port is bound to localhost on the host; the metrics port is not published. The temporary runtime directory permits runtime files while keeping the container filesystem read-only.

services:
  headscale:
    image: ghcr.io/juanfont/headscale:0.29.4
    restart: unless-stopped
    read_only: true
    tmpfs:
      - /var/run/headscale
    ports:
      - "127.0.0.1:8080:8080"
    volumes:
      - ./config:/etc/headscale:ro
      - ./lib:/var/lib/headscale
    command: serve
docker compose config
docker compose run --rm headscale configtest
docker compose up -d
docker compose logs --tail=100 headscale
curl --fail http://127.0.0.1:8080/health

A successful local health response does not prove that clients can register through the public HTTPS endpoint. Configure and test the reverse proxy separately. Headscale’s proxy guidance explains the custom protocol upgrade requirements; an ordinary web page working through a proxy is not sufficient evidence.

Register one device first

Install a supported Tailscale client on a test device. Use the current registration instructions and platform-specific guidance, rather than assuming Linux commands and sudo apply unchanged on Windows or mobile systems.

For the container server, create a user and inspect the result:

docker compose exec headscale headscale users create homelab
docker compose exec headscale headscale users list

On a Linux client, replace the server hostname before starting interactive registration:

sudo tailscale up --login-server=https://headscale.example.com

The registration page explains how to approve the node on your Headscale server. Verify the request and use the actual authentication ID shown. For non-interactive registration, current documentation uses the numeric user ID when creating a pre-authentication key. The default key is single-use and expires after one hour; reusable keys are a separate choice.

Treat pre-authentication keys and management API keys as credentials. Do not place them in article screenshots or shared Compose files. A community web dashboard is optional and needs its own configuration and access controls; it is not a built-in admin interface supplied by this Compose example.

Verify access, policy and DNS

Register a second test device and confirm the intended application works. Check the actual connection type using supported client tools. A relay can affect throughput, and a successful ping alone does not test a file share’s authentication or permissions.

Review the access policy before adding family devices or sensitive services. Test both permitted and denied access. Registering a device should not be confused with granting it the right to every household application.

MagicDNS options belong under the dns section of the full configuration. Its base domain must differ from the server URL’s domain. Follow the DNS guidance, then test a peer by address and by name separately. A name resolving successfully does not guarantee application reachability.

To reach devices that cannot run the client, such as some printers or IoT equipment, a subnet router requires deliberate route advertisement, approval, forwarding and policy. Joining two laptops does not automatically expose the entire home LAN or carry every local discovery broadcast. An exit node for internet traffic is another separately configured feature.

Maintain and recover the service

Back up the configuration, policy and persistent database/private-key data using an appropriate consistent procedure. For a simple SQLite folder copy, stop the service first and restart it after the copy. Keep the backup off the coordinator’s disk and test recovery without replacing the live installation.

Follow the upgrade guidance for supported version progression. Read release notes, preserve a working configuration and check registration, DNS, policy and a real application after updating. Headscale can be useful for a home network when those operating responsibilities suit you.

Visit the official Headscale website.