Headscale Control Server On Loopback, Proven By A Pre-Auth Key
Headscale, the self-hosted Tailscale control server, from the upstream release binary (sha256-verified) as a hardened system service on loopback with sqlite; configtest runs as the service user (it opens the database). The live test creates a user over the unix socket, issues a reusable pre-auth key for it, reads the user back from the key, destroys it. Original role, live-tested on Rocky 10.
Verification
Live-testedReally deployed to a container sandbox, proven idempotent (a second run changes nothing), verified against the role’s assertions, then torn down.
Conformance
- Static validation (yamllint · ansible-lint)
Provenance
- SHA-256 checksum
- Signature (pending)
Functional
- Live-tested - applied, verified, destroyed
Last verified 2026-09-20 · podman 4.9.3 · ansible 2.21.4 · how we verify
Documentation
headscale
Headscale, the self-hosted Tailscale control server, from the upstream release binary (sha256-verified), as a hardened system service on loopback with sqlite, for a proxy that terminates TLS. Original role for EL 10, live-tested with podman on Rocky Linux 10.
No package, so the checksum is the whole story. EL 10 carries no
headscale; Headscale ships a release with a checksum file beside it. The
role downloads both and has Ansible's get_url refuse the binary unless its
SHA-256 is the one in the vendor's file, then installs it as
root's in /usr/local/bin, pinned by headscale_version.
A service account, a hardened unit, a loopback listener. headscale
is a system user with no shell that owns the data directory and nothing
else; the unit runs with NoNewPrivileges, PrivateTmp, ProtectHome and
ProtectSystem=strict. The listener is 127.0.0.1:8080 by default,
for a proxy that authenticates or a client on the same host; the live test
reads the listening sockets and expects loopback only.
configtest runs as the service. headscale configtest opens the
database and creates the noise private key while it checks the file, so
run by root (the obvious validate:) it leaves root-owned files in the
data directory and the service dies at start unable to read them. The
role runs it as the headscale user after the file lands, before the
unit is touched. Two keys the older guides show, randomize_client_port
and ephemeral_node_inactivity_timeout, were removed in 0.29 and refuse
the start; the role writes the current shape.
Proven by a key. The live test creates a user through the CLI over the
unix socket, lists the users back as JSON, issues a reusable
pre-authentication key for that user (the thing a new node joins with),
reads the user's name back from the key, and destroys the user. The
listener is loopback; headscale_server_url is what clients are told,
so behind the proxy it is the public https address. Metrics are on
127.0.0.1:9090, the vendor's default and Prometheus's port: change one
if both run on a host.
License
Commercial - IaC Bazaar EULA. (c) IaC Bazaar.
Usage code & full reference need an account
The complete copy-paste usage, the full input/output reference, and operational notes are free with an account - shown here and bundled in the download. Sign in and this section fills in.
- Variables
- Test