Skip to content

headscale

v0.29.7
juanfont/headscale

An open source, self-hosted implementation of the Tailscale control server

Version of juanfont/headscale you run

CompareView on GitHub

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 GivenName collision 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 literal node instead of invalid-. MagicDNS names change on upgrade for any node whose previous label was a random-suffix form; the raw Hostname column 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/0 and ::/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-all as a source, or explicit CIDR ranges as destinations #2180
    • autogroup:danger-all can only be used as a source; it cannot be used as a destination
    • Note: Users with non-standard IP ranges configured in prefixes.ipv4 or prefixes.ipv6 (which is unsupported and produces a warning) will need to explicitly specify their CIDR ranges in ACL rules instead of using *
    • Validate autogroup:self source restrictions matching Tailscale behavior - tags, hosts, and IPs are rejected as sources for autogroup:self destinations #3036
    • Policies using tags, hosts, or IP addresses as sources for autogroup:self destinations will now fail validation
    • The proto:icmp protocol name now only includes ICMPv4 (protocol 1), matching Tailscale behavior #3036
    • Previously, proto:icmp included both ICMPv4 and ICMPv6
    • Use proto:ipv6-icmp or protocol number 58 explicitly 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_port server-config key was removed; the

    toggle now lives in the policy file as a top-level

    randomizeClientPort field, 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, and ValidTags fields have been removed and replaced with a single Tags field that contains the node's applied tags #2993
    • API clients should use the Tags field instead of ValidTags
    • The headscale nodes list CLI command now always shows a Tags column and the --tags flag has been removed
    • PreAuthKey CLI: Commands now use ID-based operations instead of user+key combinations #2992
    • headscale preauthkeys create no longer requires --user flag (optional for tracking creation)
    • headscale preauthkeys list lists all keys (no longer filtered by user)
    • headscale preauthkeys expire --id replaces --user
    • headscale preauthkeys delete --id replaces --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 1
    

    After:

      headscale preauthkeys create --reusable --tags tag:server
      headscale preauthkeys list
      headscale preauthkeys expire --id 123
      headscale preauthkeys delete --id 123
    
    • Tags: The gRPC SetTags endpoint now allows converting user-owned nodes to tagged nodes by setting tags. #2885
    • Tags: Tags are now resolved from the node's stored Tags field only #2931
  • v0.27.0Oct 27, 2025

    BREAKING

    • Remove support for 32-bit binaries

    #2692

    • Policy: Zero or empty destination port is no longer allowed

    #2606

    • 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-XXXXXX format

    • my-laptop → accepted as-is
    • My-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)

    #2405

Release history

All releases on GitHub
BadgeLatest version of juanfont/headscale[![version](/badge/juanfont/headscale)](/p/juanfont/headscale)

Minimum 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_addr disables 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/32 or ::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_verified flag without an address no longer verifies another document's email #3603
  • Fix release builds reporting their version with a +dirty suffix #3584
  • Fix SSH policy acceptEnv having no effect, so allowed environment variables reach SSH sessions. Upgrade SSH destination nodes to Tailscale v1.104.4 or newer before using acceptEnv, 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

Full release notesCompare with previous