headscale
v0.29.7An open source, self-hosted implementation of the Tailscale control server
- Maintenance
- ActiveBased on the last push: commit history is not collected yet
- License
- BSD-3-ClausePermissive · OSI-approved
- Security
- No advisories publishedScorecard 5.3/10
- Supported versions
- No scheduleendoflife.date does not track this project
45.7k stars · 2.6k forks · 84 open issues · last release 16 days ago · data checked 4 hours ago
Breaking changes
What to check before upgrading, taken from the notes of the last 30 releases.
- v0.29.0Jun 17, 2026
- all IPs (see BREAKING below). The name is intentional: accepting traffic from the entire
BREAKING
Hostname handling
- The
GivenNamecollision policy changed from an 8-char random hash suffix (laptop-abc12xyz) to a monotonic numeric suffix (laptop,laptop-1,laptop-2, …), matching Tailscale SaaS. Empty / all-non-ASCII hostnames now fall back to the literalnodeinstead ofinvalid-. MagicDNS names change on upgrade for any node whose previous label was a random-suffix form; the rawHostnamecolumn is unchanged. #3202
ACL Policy
- Wildcard (
*) in ACL sources and destinations now resolves to Tailscale's CGNAT range (100.64.0.0/10) and ULA range (fd7a:115c:a1e0::/48) instead of all IPs (0.0.0.0/0and::/0) #3036 - This better matches Tailscale's security model where
*means "any node in the tailnet" rather than "any IP address" - Policies that need to match all IP addresses including non-Tailscale IPs should use
autogroup:danger-allas a source, or explicit CIDR ranges as destinations #2180 autogroup:danger-allcan only be used as a source; it cannot be used as a destination- Note: Users with non-standard IP ranges configured in
prefixes.ipv4orprefixes.ipv6(which is unsupported and produces a warning) will need to explicitly specify their CIDR ranges in ACL rules instead of using* - Validate
autogroup:selfsource restrictions matching Tailscale behavior - tags, hosts, and IPs are rejected as sources forautogroup:selfdestinations #3036 - Policies using tags, hosts, or IP addresses as sources for
autogroup:selfdestinations will now fail validation - The
proto:icmpprotocol name now only includes ICMPv4 (protocol 1), matching Tailscale behavior #3036 - Previously,
proto:icmpincluded both ICMPv4 and ICMPv6 - Use
proto:ipv6-icmpor protocol number58explicitly for ICMPv6
Upgrade Path
- Headscale now enforces a strict version upgrade path #3083
- Skipping minor versions (e.g. 0.27 → 0.29) is blocked; upgrade one minor version at a time
- Downgrading to a previous minor version is blocked
- Patch version changes within the same minor are always allowed
Configuration
- The
randomize_client_portserver-config key was removed; the
toggle now lives in the policy file as a top-level
randomizeClientPortfield, matching the Tailscale-hosted schema. #3251 - v0.28.0Feb 4, 2026
Database migration support removed for pre-0.25.0 databases
Headscale no longer supports direct upgrades from databases created before version 0.25.0. Users on older versions must upgrade
sequentially through each stable release, selecting the latest patch version available for each minor release.
BREAKING
- API: The Node message in the gRPC/REST API has been simplified - the
ForcedTags,InvalidTags, andValidTagsfields have been removed and replaced with a singleTagsfield that contains the node's applied tags #2993 - API clients should use the
Tagsfield instead ofValidTags - The
headscale nodes listCLI command now always shows a Tags column and the--tagsflag has been removed - PreAuthKey CLI: Commands now use ID-based operations instead of user+key combinations #2992
headscale preauthkeys createno longer requires--userflag (optional for tracking creation)headscale preauthkeys listlists all keys (no longer filtered by user)headscale preauthkeys expire --idreplaces--userheadscale preauthkeys delete --idreplaces--user
Before:
headscale preauthkeys create --user 1 --reusable --tags tag:server headscale preauthkeys list --user 1 headscale preauthkeys expire --user 1 headscale preauthkeys delete --user 1After:
headscale preauthkeys create --reusable --tags tag:server headscale preauthkeys list headscale preauthkeys expire --id 123 headscale preauthkeys delete --id 123 - API: The Node message in the gRPC/REST API has been simplified - the
- v0.27.0Oct 27, 2025
BREAKING
- Remove support for 32-bit binaries
- Policy: Zero or empty destination port is no longer allowed
- Stricter hostname validation #2383
- Hostnames must be valid DNS labels (2-63 characters, alphanumeric and
hyphens only, cannot start/end with hyphen)
- Client Registration (New Nodes): Invalid hostnames are automatically
renamed to
invalid-XXXXXXformatmy-laptop→ accepted as-isMy-Laptop→my-laptop(lowercased)my_laptop→invalid-a1b2c3(underscore not allowed)test@host→invalid-d4e5f6(@ not allowed)laptop-🚀→invalid-j1k2l3(emoji not allowed)- Hostinfo Updates / CLI: Invalid hostnames are rejected with an error
- Valid names are accepted or lowercased
- Names with invalid characters, too short (<2), too long (>63), or
starting/ending with hyphen are rejected
- v0.26.0May 14, 2025
BREAKING
Routes
Route internals have been rewritten, removing the dedicated route table in the
database. This was done to simplify the codebase, which had grown unnecessarily
complex after the routes were split into separate tables. The overhead of having
to go via the database and keeping the state in sync made the code very hard to
reason about and prone to errors. The majority of the route state is only
relevant when headscale is running, and is now only kept in memory. As part of
this, the CLI and API has been simplified to reflect the changes;
$ headscale nodes list-routes ID | Hostname | Approved | Available | Serving (Primary) 1 | ts-head-ruqsg8 | | 0.0.0.0/0, ::/0 | 2 | ts-unstable-fq7ob4 | | 0.0.0.0/0, ::/0 | $ headscale nodes approve-routes --identifier 1 --routes 0.0.0.0/0,::/0 Node updated $ headscale nodes list-routes ID | Hostname | Approved | Available | Serving (Primary) 1 | ts-head-ruqsg8 | 0.0.0.0/0, ::/0 | 0.0.0.0/0, ::/0 | 0.0.0.0/0, ::/0 2 | ts-unstable-fq7ob4 | | 0.0.0.0/0, ::/0 |Note that if an exit route is approved (0.0.0.0/0 or ::/0), both IPv4 and IPv6
will be approved.
- Route API and CLI has been removed
- v0.25.0Feb 11, 2025
BREAKING
- Authentication flow has been rewritten
#2374 This change should be
transparent to users with the exception of some buxfixes that has been
discovered and was fixed as part of the rewrite.
- When a node is registered with _a new user_, it will be registered as a new
node (#2327 and
#1310).
- A logged out node logging in with the same user will replace the existing
node.
- Remove support for Tailscale clients older than 1.62 (Capability version 87)
Release history
All releases on GitHubMinimum supported Tailscale client version: v1.80.0
Security
This is a security release. It fixes findings from Anthropic's oss-scanner project and
the Tailworm report. None are critical, but some can cause denial of service in
Headscale. We recommend that all users upgrade to Headscale 0.29.7 and Tailscale
v1.104.4 or newer as soon as possible, especially those using Tailscale SSH.
Tailscale v1.104.4 fixes vulnerabilities in SSH forwarding, SSH session handling and
Serve. These fixes run in tailscaled on your nodes; upgrading Headscale alone does
not apply them.
See Tailscale's security bulletins and Tailscale SSH: Findings and fixes.
To disable SSH agent and port forwarding on a node, set
TS_SSH_DISABLE_FORWARDING=1 in the tailscaled service environment and restart
the service. This setting applies to all Tailscale SSH sessions on that node and
is also available in older clients. #3624
This release also fixes the issues that oss-scanner
reported in Headscale: ANT-2026-MLP8ZR5T, ANT-2026-LYVGXVSW, ANT-2026-AWGRB7O1, ANT-2026-QMCAFJ1Q, ANT-2026-RQS7JAPP, ANT-2026-5B6KE87K, ANT-2026-KFCFFM34, ANT-2026-K903ZEUA, ANT-2026-NM50ZP4N, ANT-2026-ZC18AF82, ANT-2026-NWTIBB02, ANT-2026-MX8FKG4K. #3603
Changes
- Metrics are no longer exposed to unregistered clients through the Noise protocol. They remain available only on the configured metrics listener; an empty
metrics_listen_addrdisables that listener #3623 - Update the Tailscale dependency to v1.104.4, which bounds how much of an unexpected DERP HTTP response is read #3577
- Keep pending registrations and OIDC logins at capacity instead of evicting ones awaiting approval; new requests get a retry response, and registered nodes keep capacity for SSH checks #3586
- Cap stored pending registration data at 64 KiB, keeping tags and expiry; oversized interactive registrations are rejected #3586
- Check the OIDC login type before storing anything, accept each OIDC callback only once, and free an SSH check's slot once the client has read its result #3586
- Stop serving a route that is a single address inside the node IP ranges, such as
100.64.0.11/32or::ffff:100.64.0.11/128, because it names a node, not a subnet; the route stays approved and listed, and wider routes are still served #3603 - Require a valid pre-auth key to re-register an expired tagged node #3603
- Another user's key or login on a tagged machine now creates a new device for that user instead of re-authenticating the tagged node #3603
- Fix one node with a stored key expiry outside the years 0 to 9999 in UTC failing the map of every node that can see it; it is now left out of its peers' maps and the startup check names it with the fix command #3603
- Refuse a key expiry outside the years 0 to 9999 in UTC on registration, on the OIDC token-expiry path and in
headscale nodes expire/POST /api/v1/node/{node_id}/expire(HTTP 400); the latest accepted expiry no longer fails node listings on PostgreSQL in a zone east of UTC #3603 - Keep the first 256 endpoints and 4096 announced routes from client reports; oversized hostnames, requested tags or other metadata return a registration error or HTTP 413 on map updates. Zoned IPv6 endpoints are refused #3603
- At startup, trim stored hostnames, endpoints and routes to the client-report limits and remove zoned endpoints. Hostinfo exceeding its size or tag limit is dropped until the next valid report #3603
- Pending interactive registrations and follow-up waits keep only the hostname, OS and requested tags; other device details arrive with the first map request. Reauthentication preserves routes, services and endpoints #3603
- Fix web reauthentication dropping the discovery key, so peers could not reach the device #3603
- A stalled request body, or a Noise tunnel with no open request, is closed after 30 seconds #3603
- A registration follow-up from a machine other than the one that started the registration is refused before it waits #3603
- Only the newest follow-up waits on a pending registration or SSH check; older ones are answered at once #3603
- Fix registration locks staying in memory after the registration ends #3603
- Fix concurrent deletes of one node freeing its addresses and announcing its removal more than once #3603
- OIDC verifies an email against the document supplying it; an
email_verifiedflag without an address no longer verifies another document's email #3603 - Fix release builds reporting their version with a
+dirtysuffix #3584 - Fix SSH policy
acceptEnvhaving no effect, so allowed environment variables reach SSH sessions. Upgrade SSH destination nodes to Tailscale v1.104.4 or newer before usingacceptEnv, because older clients can disclose forwarded values #3569
Upgrade
Please follow the steps outlined in the upgrade guide to update your existing Headscale installation.
Changelog
- c0a51b2cbd1bb17d604b506d5d6a06533a9c7b3f .gitattributes: mark golden data as generated
- c9cf210b8bc84d13177e18b91f555788a44b47b6 CHANGELOG: align the 0.29.7 entries with earlier releases
- 3d9031c075d287e9aa1b5c2264276a40e7335f8a CHANGELOG: document SSH acceptEnv fix
- 508355901605e89c020636daf911f5f09b702982 CHANGELOG: document the 0.29.7 security and patch release
- 8651eb9b1a6170153838d16b39871aef6019a116 CHANGELOG: list the oss-scanner findings for 0.29.7
- abe807a9b5b50160618d6c3345f3a8d8055bce74 CHANGELOG: list the pending authentication bounds for 0.29.7
- 3345493f928745f14aa5cd21c1ae71f34ea47201 auth, state: keep only hostname, OS and tags in pending registrations
- 1b7435c57f5d3c68ec4deba2576f819a30a7e853 auth: check the follow-up's machine key before it waits
- 99d10b9b890737aedeeae049d820f00461326e81 db, state: let only the delete that removed a node free its addresses
- 151c852d04a440f60d5eee6aa997ae77924a338e go.mod: pin golang-lru/v2 to main for expirable.Close
- 718cbc3b046dfa02e90640d90dabe355eb482f32 go.mod: tidy for Go 1.27
- 044d8f5a01f53c79403c8d092a37d34c8a6e1803 go.mod: update tailscale.com to v1.104.4
- 37b4237ef617f4176b69d46c2c50bd347fa5b84c hscontrol: bound pending authentication without evicting sessions
- be114c0d632599f538a3f7c441a1cca92c80ac5c hscontrol: refuse a key expiry that cannot be encoded
- c32bf08d0905a041ea768e2cd32cb8225ceae9e4 hscontrol: remove unused OIDC user change result
- 3bb6d3a756556ed09cb203cfc1c4eb82d3be676c integration/tsic: drop unused watchIPN
- fd6547f24941513c4679a56c007eff675cc3ce0e integration: guard SSH acceptEnv forwarding
- 15af45ca08db05282650095eaaa541611a2736ec mapper: skip peers whose key expiry cannot be encoded