github.com/juanfont/headscale
v0.29.3
#43 most downloaded on Go modules
juanfont/headscale
What this package is like to depend on
Last release 25 days ago
29 Jul 2026
Ships fairly regularly
a new release about every 3 weeks
Some releases are documented
notes for 37 of 75 stable releases
Nothing withdrawn
no release was ever pulled
6 years old
552 releases · first in 2020
58 releases in the last 12 months
see the full history below
Release timeline
552 releases · Aug 2020 to Jul 2026Releases
latest 60 of 552-
v0.29.329 Jul 2026Release notes
Open source →Minimum supported Tailscale client version: v1.80.0
Changes
- Fix tagged node stuck expired after
tailscale logout, unable to re-authenticate #3394 - Re-registering a tagged node with a different pre-auth key now applies the new key's tags instead of silently keeping the old ones #3394
- Fix re-authenticating an already-tagged node with
--advertise-tagsbeing rejected when the authenticating user owns the tags #3394 - Fix ephemeral nodes lingering as disconnected after reconnect churn #3383
- Fix node registration falsely returning
401 registration timed outwhen auth completes as the request context expires #3392 - Check the machine key on the followup registration poll so a leaked auth ID cannot return the registering user's identity #3393
- Reject
/keyrequests below the supported capability version floor, matching/ts2021#3391
Upgrade
Please follow the steps outlined in the upgrade guide to update your existing Headscale installation.
Changelog
- 235a57e CHANGELOG: add 0.29.3
- 089d6c4 Explicitly select lunr as search provider
- fba84ca auth: check machine key on the followup registration path
- 1292841 build: bump Go toolchain to 1.26.5
- 9609a0b hscontrol: gate /key on supported capability version
- bdc3e99 hscontrol: prefer completed auth over expired ctx in followup wait
- 5aff68b mkdocs: bump version
- 4a1e773 policy,state: authorize reauth tags against the authenticating user
- 5fb514e poll: do not cancel ephemeral GC until Connect succeeds
- 1fccdb1 state: apply a new pre-auth key's tags on re-registration
- d202883 state: do not expire tagged nodes on logout
Release notes
Open source →Minimum supported Tailscale client version: v1.80.0
Changes
- Fix tagged node stuck expired after
tailscale logout, unable to re-authenticate #3394 - Re-registering a tagged node with a different pre-auth key now applies the new key's tags instead of silently keeping the old ones #3394
- Fix re-authenticating an already-tagged node with
--advertise-tagsbeing rejected when the authenticating user owns the tags #3394 - Fix ephemeral nodes lingering as disconnected after reconnect churn #3383
- Fix node registration falsely returning
401 registration timed outwhen auth completes as the request context expires #3392 - Check the machine key on the followup registration poll so a leaked auth ID cannot return the registering user's identity #3393
- Reject
/keyrequests below the supported capability version floor, matching/ts2021#3391 - Remove a leftover trace log that always rendered a JSON marshaling error #3398
Release notes
Open source →Minimum supported Tailscale client version: v1.80.0
Changes
- Fix tagged node stuck expired after
tailscale logout, unable to re-authenticate #3394 - Re-registering a tagged node with a different pre-auth key now applies the new key's tags instead of silently keeping the old ones #3394
- Fix re-authenticating an already-tagged node with
--advertise-tagsbeing rejected when the authenticating user owns the tags #3394 - Fix ephemeral nodes lingering as disconnected after reconnect churn #3383
- Fix node registration falsely returning
401 registration timed outwhen auth completes as the request context expires #3392 - Check the machine key on the followup registration poll so a leaked auth ID cannot return the registering user's identity #3393
- Reject
/keyrequests below the supported capability version floor, matching/ts2021#3391
- Fix tagged node stuck expired after
-
v0.29.201 Jul 2026Release notes
Open source →Changes
- Fix map generation serializing on the policy lock, so a mass reconnect on
autogroup:self, via or relay policies no longer stalls clients intounexpected EOFretry loops #3358 - Fix
/ts2021rejecting the WebSocketGETupgrade with 405, which prevented Tailscale JS/WASM control clients from connecting #3359 - Gracefully handle nodes with an invalid FQDN (empty or too long) instead of failing map delivery; offending names are logged at startup with the fix command #3349
Upgrade
Please follow the steps outlined in the upgrade guide to update your existing Headscale installation.
Changelog
- 8eea894 CHANGELOG: drop unreleased 0.30.0 stub
- f708c5b CHANGELOG: note /ts2021 WebSocket GET fix
- 735742e CHANGELOG: note 0.29.2 invalid-name map fix
- 3ac33cf CHANGELOG: shorten 0.29.2 invalid-name entry, set date
- f885d87 Fix invalid ip syntax
- 1ec7b7f hscontrol: register /ts2021 for WebSocket GET
- 8f4e69d integration: add TS2021 WebSocket tests to CI matrix
- e7851ef integration: test /ts2021 WebSocket GET with a real WASM client
- 9d13274 mapper,policy: add reconnect-storm and lock-concurrency regression tests
- ec67197 mapper: skip peers with invalid names instead of failing the map
- d4f2acf policy: take RLock for reads so map generation runs concurrently
- 5fb76eb poll: return an HTTP error on long-poll setup failure
- fd154fd state: log nodes with map-breaking data at startup
- 9c9206a state: reject renames whose FQDN exceeds the hostname limit
Release notes
Open source →Minimum supported Tailscale client version: v1.80.0
Changes
- Fix map generation serializing on the policy lock, so a mass reconnect on
autogroup:self, via or relay policies no longer stalls clients intounexpected EOFretry loops #3358 - Fix
/ts2021rejecting the WebSocketGETupgrade with 405, which prevented Tailscale JS/WASM control clients from connecting #3359 - Gracefully handle nodes with an invalid FQDN (empty or too long) instead of failing map delivery; offending names are logged at startup with the fix command #3349
- Fix map generation serializing on the policy lock, so a mass reconnect on
-
v0.29.2-0.20260701144739-8eea89488c6401 Jul 2026 pre-releaseNothing published for this version
-
v0.29.118 Jun 2026Release notes
Open source →Minimum supported Tailscale client version: v1.80.0
Changes
- Fix nodes with
tags='null'losing their assigned user on upgrade #3325
Upgrade
Please follow the steps outlined in the upgrade guide to update your existing Headscale installation.
Changelog
- 636f660 db: preserve user_id on untagged nodes with tags='null'
Release notes
Open source →Minimum supported Tailscale client version: v1.80.0
Changes
- Fix nodes with
tags='null'losing their assigned user on upgrade #3325
- Fix nodes with
-
v0.29.1-0.20260618132111-74928c02412918 Jun 2026 pre-releaseNothing published for this version
-
v0.29.017 Jun 2026Release notes
Open source →Minimum supported Tailscale client version: v1.80.0
Tailscale ACL compatibility improvements
Extensive test cases were systematically generated using Tailscale clients and the official SaaS
to understand how the packet filter should be generated. We discovered a few differences, but
overall our implementation was very close.
#3036SSH check action
SSH rules with
"action": "check"are now supported. When a client initiates a SSH connection to a node
with acheckaction policy, the user is prompted to authenticate via OIDC or CLI approval before access
is granted. OIDC approval requires the authenticated user to own the source node; tagged source nodes
cannot use SSH check-mode.A new
headscale authCLI command group supports the approval flow:headscale auth approve --auth-id <id>approves a pending authentication request (SSH check or web auth)headscale auth reject --auth-id <id>rejects a pending authentication requestheadscale auth register --auth-id <id> --user <user>registers a node (replaces deprecatedheadscale nodes register)
Policy tests (beta)
Headscale now evaluates the
testsblock in a policy file. Tests assert reachability between
named sources and destinations and cover the whole policy — bothaclsandgrantsrules
contribute. They run on user-initiated writes viaheadscale policy set, on SIGHUP reload
(systemctl reload headscale/kill -HUP $(pidof headscale)), and onheadscale policy check.
A failing test rejects the write before it is applied, with the same error message Tailscale SaaS
would return for the same policy.At boot a stored policy whose tests no longer pass — for example because a referenced user was
deleted while the server was offline — logs a warning and the server keeps running. Fix the
policy and reload.This feature is beta while behavioural coverage against Tailscale SaaS broadens.
SSH policy tests (beta)
Headscale now evaluates the
sshTestsblock in a policy file. Each entry names a source, one or
more destination hosts, and three optional user lists:acceptasserts the listed login users
reach every destination via an accept- or check-action SSH rule,denyasserts none of them
reach any destination, andcheckrequires reachability specifically through a check-action
rule. Tests run onheadscale policy set, on SIGHUP reload (systemctl reload headscale/
kill -HUP $(pidof headscale)), and onheadscale policy check. A failing test rejects the
write before it is applied, with the same error message Tailscale SaaS would return for the same
policy.At boot a stored policy whose sshTests no longer pass — for example because a referenced user was
deleted while the server was offline — logs a warning and the server keeps running. Fix the
policy and reload.This feature is beta while behavioural coverage against Tailscale SaaS broadens.
SSH rule validation
SSH rule parsing now trims surrounding whitespace on
action,users,src, anddst,
rejects empty or wildcard entries inusers, rejects emptyacceptEnv, and rejects negative
checkPeriod.hosts:aliases are rejected as SSH destinations, non-ASCII tag names are
rejected at parse time, and the wording for group-nesting cycles matches Tailscale SaaS.
#3263Grants
We now support Tailscale grants
alongside ACLs. Grants extend what you can express in a policy beyond packet filtering: theapp
field controls application-level features like Taildrive file sharing and peer relay, and thevia
field steers traffic through specific tagged subnet routers or exit nodes. Theipfield works like
an ACL rule. Grants can be mixed with ACLs in the same policy file.
#2180As part of this, we added
autogroup:danger-all. It resolves to0.0.0.0/0and::/0, all IP
addresses, including those outside the tailnet. This replaces the old behaviour where*matched
all IPs (see BREAKING below). The name is intentional: accepting traffic from the entire
internet is a security-sensitive choice.autogroup:danger-allcan only be used as a source.Node attributes (
nodeAttrs)ACL policies now accept a
nodeAttrsblock. Each entry hands a list of
Tailscale node capabilities to every node matchingtarget. The accepted
target forms are the same asacls.srcandgrants.src: users, groups,
tags, hosts, prefixes,autogroup:member,autogroup:tagged, and*.Frequently requested capabilities this unlocks include
magicdns-aaaa,
disable-relay-server,disable-captive-portal-detection,
nextdns:<profile>/nextdns:no-device-info,randomize-client-port,
and the Taildrivedrive:share/drive:accesspair. The set is not
limited to these, any string-only cap an operator places in policy
reaches clients unchanged.randomizeClientPortalso lands as a top-level policy field that toggles
the default for every node, replacing the old server-config knob.A new
auto_update.enabledconfig option controls the tailnet-wide
default for client auto-update. When true, every node's CapMap carries
default-auto-update: [true]so fresh clients pick up the default
unless they make a local opt-in / opt-out choice.Policies that use the
funnelcap,ipPoolblocks, or
autogroup:admin/autogroup:ownertargets are rejected at load —
those features depend on machinery headscale does not yet ship.Taildrive
Taildrive (file-sync between
nodes) is now
configurable through policy. Grantdrive:shareto the node that
hosts files anddrive:accessto nodes that read or write them; pair
with atailscale.com/cap/drivegrant to set the per-share access
mode:{ "nodeAttrs": [ { "target": ["tag:fileserver"], "attr": ["drive:share"] }, { "target": ["autogroup:member"], "attr": ["drive:access"] }, ], "grants": [ { "src": ["autogroup:member"], "dst": ["tag:fileserver"], "app": { "tailscale.com/cap/drive": [{ "shares": ["*"], "access": "rw" }], }, }, ], }A wildcard
nodeAttrs("target": ["*"]) hands the caps to every
node when fine-grained control is not needed.Hostname sanitisation
Hostnames are now santised using Tailscales
magicdnssanitisation rules, matching Tailscale SaaS behavior. This means that hostnames with non-ASCII characters, special characters, or reserved DNS label characters are now transformed into valid DNS labels for MagicDNS. This improves our previously too strict sanitisation that rejected hostnames based on our guesswork and not based on the Tailscale upstream behaviour.Examples that previously regressed and now work:
Input Raw (Hostname) DNS label (GivenName) Joe's Mac miniJoe's Mac minijoes-mac-miniYuri's MacBook ProYuri's MacBook Proyuris-macbook-proTest@HostTest@Hosttest-hostmail.servermail.servermail-serverMy-PC!My-PC!my-pc我的电脑我的电脑nodeHA subnet router health probing
Headscale now actively probes HA subnet routers to detect nodes that are connected but not
forwarding traffic. The control plane periodically pings HA subnet routers via the Noise
control channel and fails over to a healthy standby if the primary stops responding. This is
enabled by default (node.routes.ha.probe_interval: 10s,probe_timeout: 5s) and only
active when HA routes exist (2+ nodes advertising the same prefix). Setprobe_intervalto
0to disable. This complements the existing disconnect-based failover, catching "zombie
connected" routers that maintain their control session but cannot route packets.
#3194BREAKING
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-<rand>. 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*
- This better matches Tailscale's security model where
- 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
- Policies using tags, hosts, or IP addresses as sources for
- 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
- Previously,
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
Headscale refuses to start when the old key is set. Move it to the
policy file referenced bypolicy.path:{ "randomizeClientPort": true, }If you do not have a policy file yet, create one with that minimal
content and pointpolicy.pathat it. The default carries over —
empty / absent policy meansrandomizeClientPort: false, matching
the previous behaviour for operators who never set the key. Per-node
opt-in vianodeAttrsis also supported and stacks on top of the
global default.
CLI
headscale nodes registeris deprecated in favour ofheadscale auth register --auth-id <id> --user <user>#1850- The old command continues to work but will be removed in a future release
Changes
ACL Policy
- Fix subnet-to-subnet peer visibility — subnet routers now correctly become peers when ACL rules reference only subnet CIDRs as sources, without requiring node IP rules #3175
- Fix filter rule reduction to use only approved subnet routes instead of all advertised routes, matching Tailscale SaaS behavior #3175
- Add ICMP and IPv6-ICMP protocols to default filter rules when no protocol is specified #3036
- Fix autogroup:self handling for tagged nodes - tagged nodes no longer incorrectly receive autogroup:self filter rules #3036
- Use CIDR format for autogroup:self destination IPs matching Tailscale behavior #3036
- Merge filter rules with identical SrcIPs and IPProto matching Tailscale behavior - multiple ACL rules with the same source now produce a single FilterRule with combined DstPorts #3036
- Fix exit nodes incorrectly receiving filter rules for destinations that only overlap via exit routes #3169 #3175
- Fix address-based aliases (hosts, raw IPs) incorrectly expanding to include the matching node's other address family #2180
- Fix identity-based aliases (tags, users, groups) resolving to IPv4 only; they now include both IPv4 and IPv6 matching Tailscale behavior #2180
- Fix wildcard (
*) source in ACLs now using actually-approved subnet routes instead of autoApprover policy prefixes #2180 - Fix non-wildcard source IPs being dropped when combined with wildcard
*in the same ACL rule #2180 - Fix exit node approval not triggering filter rule recalculation for peers #2180
- Policy validation error messages now include field context (e.g.,
src=,dst=) and are more descriptive #2180 - Reject policies whose
user@tokens match multiple DB users; rename the duplicate viaheadscale users renameto load #3160 - Evaluate the policy
testsblock on user-initiated writes across bothaclsandgrants; reject policies whose tests fail (beta) #1803
Grants
- Add support for policy grants with
ip,app, andviafields #2180 - Add
autogroup:danger-allas a source-only autogroup resolving to all IP addresses #2180 - Add capability grants for Taildrive (
cap/drive) and peer relay (cap/relay) with automatic companion capabilities #2180 - Add per-viewer via route steering — grants with
viatags control which subnet router or exit node handles traffic for each group of viewers #2180 - Enable Taildrive node attributes on all nodes; actual access is controlled by
cap/drivegrants #2180
SSH Policy
- Add support for
localpart:*@<domain>in SSH ruleusersfield, mapping each matching user's email local-part as their OS username #3091 - Add SSH
checkaction support with OIDC and CLI-based approval flows #1850
CLI
- Add
headscale auth register,headscale auth approve, andheadscale auth rejectCLI commands #1850 - Deprecate
headscale nodes register --keyin favour ofheadscale auth register --auth-id#1850 headscale policy check --bypass-grpc-and-access-database-directlyvalidatesuser@tokens against the live user database #3160- Remove deprecated
--namespaceflag fromnodes list,nodes register, anddebug create-nodecommands (use--userinstead) #3093 - Remove deprecated
namespace/nscommand aliases forusersandmachine/machinesaliases fornodes#3093 - Fix
DestroyUserdeleting all pre-auth keys in the database instead of only the target user's keys #3155 headscale policy checkevaluates thetestsblock when invoked with--bypass-grpc-and-access-database-directly; without the flag it warns instead of running the tests against empty data #1803
API
- Add
authrelated routes. Theauth/registerendpoint now expects data as JSON #1850 - Remove gRPC reflection from the remote (TCP) server #3180
OIDC
- Add a confirmation page before completing node registration, showing the device hostname and machine key fingerprint #3180
- Generalise auth templates into reusable
AuthSuccessandAuthWebcomponents #1850 - Unify auth pipeline with
AuthVerdicttype, supporting registration, reauthentication, and SSH checks #1850
Configuration
- Add
node.expiryconfiguration option to set a default node key expiry for nodes registered via auth key #3122- Tagged nodes (registered with tagged pre-auth keys) are exempt from default expiry
oidc.expiryhas been removed; usenode.expiryinstead (applies to all registration methods including OIDC)ephemeral_node_inactivity_timeoutis deprecated in favour ofnode.ephemeral.inactivity_timeout
- Add
trusted_proxiesto gateTrue-Client-IP/X-Real-IP/X-Forwarded-For(previously honoured from any client) #3268
Debug
- Add node connectivity ping page for verifying control-plane reachability #3183
- Omit secret fields (
Pass,ClientSecret,APIKey) from/debug/configJSON output #3180 - Route
statsvizthroughtsweb.Protected#3180
Other
- Remove old migrations for the debian package #3185
- Install
config-example.yamlas example for the debian package #3186 - Fix user-owned re-registration with zero client expiry and no default storing
0001-01-01 00:00:00in the database instead ofNULL#3199 - Fix
tailscaledrestart on a node with no expiry resettingNULLto0001-01-01 00:00:00in the database, affecting both tagged and untagged nodes #3197 - Backfill
nodes.expiryrows persisted by older versions as0001-01-01 00:00:00toNULL, so nodes upgraded from <0.28 stop reporting as expired #3284 - Update reverse proxy documentation for
trusted_proxiesconfiguration option #3292
Upgrade
Please follow the steps outlined in the upgrade guide to update your existing Headscale installation.
Changelog
- b0c221f changelog: set 0.29 date
Release notes
Open source →Minimum supported Tailscale client version: v1.80.0
Tailscale ACL compatibility improvements
Extensive test cases were systematically generated using Tailscale clients and the official SaaS to understand how the packet filter should be generated. We discovered a few differences, but overall our implementation was very close. #3036
SSH check action
SSH rules with
"action": "check"are now supported. When a client initiates a SSH connection to a node with acheckaction policy, the user is prompted to authenticate via OIDC or CLI approval before access is granted. OIDC approval requires the authenticated user to own the source node; tagged source nodes cannot use SSH check-mode.A new
headscale authCLI command group supports the approval flow:headscale auth approve --auth-id <id>approves a pending authentication request (SSH check or web auth)headscale auth reject --auth-id <id>rejects a pending authentication requestheadscale auth register --auth-id <id> --user <user>registers a node (replaces deprecatedheadscale nodes register)
Policy tests (beta)
Headscale now evaluates the
testsblock in a policy file. Tests assert reachability between named sources and destinations and cover the whole policy — bothaclsandgrantsrules contribute. They run on user-initiated writes viaheadscale policy set, on SIGHUP reload (systemctl reload headscale/kill -HUP $(pidof headscale)), and onheadscale policy check. A failing test rejects the write before it is applied, with the same error message Tailscale SaaS would return for the same policy.At boot a stored policy whose tests no longer pass — for example because a referenced user was deleted while the server was offline — logs a warning and the server keeps running. Fix the policy and reload.
This feature is beta while behavioural coverage against Tailscale SaaS broadens.
SSH policy tests (beta)
Headscale now evaluates the
sshTestsblock in a policy file. Each entry names a source, one or more destination hosts, and three optional user lists:acceptasserts the listed login users reach every destination via an accept- or check-action SSH rule,denyasserts none of them reach any destination, andcheckrequires reachability specifically through a check-action rule. Tests run onheadscale policy set, on SIGHUP reload (systemctl reload headscale/kill -HUP $(pidof headscale)), and onheadscale policy check. A failing test rejects the write before it is applied, with the same error message Tailscale SaaS would return for the same policy.At boot a stored policy whose sshTests no longer pass — for example because a referenced user was deleted while the server was offline — logs a warning and the server keeps running. Fix the policy and reload.
This feature is beta while behavioural coverage against Tailscale SaaS broadens.
SSH rule validation
SSH rule parsing now trims surrounding whitespace on
action,users,src, anddst, rejects empty or wildcard entries inusers, rejects emptyacceptEnv, and rejects negativecheckPeriod.hosts:aliases are rejected as SSH destinations, non-ASCII tag names are rejected at parse time, and the wording for group-nesting cycles matches Tailscale SaaS. #3263Grants
We now support Tailscale grants alongside ACLs. Grants extend what you can express in a policy beyond packet filtering: the
appfield controls application-level features like Taildrive file sharing and peer relay, and theviafield steers traffic through specific tagged subnet routers or exit nodes. Theipfield works like an ACL rule. Grants can be mixed with ACLs in the same policy file. #2180As part of this, we added
autogroup:danger-all. It resolves to0.0.0.0/0and::/0, all IP addresses, including those outside the tailnet. This replaces the old behaviour where*matched all IPs (see BREAKING below). The name is intentional: accepting traffic from the entire internet is a security-sensitive choice.autogroup:danger-allcan only be used as a source.Node attributes (
nodeAttrs)ACL policies now accept a
nodeAttrsblock. Each entry hands a list of Tailscale node capabilities to every node matchingtarget. The accepted target forms are the same asacls.srcandgrants.src: users, groups, tags, hosts, prefixes,autogroup:member,autogroup:tagged, and*.{ "randomizeClientPort": true, "nodeAttrs": [ { "target": ["autogroup:tagged"], "attr": ["disable-captive-portal-detection"] }, { "target": ["[email protected]"], "attr": ["nextdns:abc123"] }, ], }Frequently requested capabilities this unlocks include
magicdns-aaaa,disable-relay-server,disable-captive-portal-detection,nextdns:<profile>/nextdns:no-device-info,randomize-client-port, and the Taildrivedrive:share/drive:accesspair. The set is not limited to these, any string-only cap an operator places in policy reaches clients unchanged.randomizeClientPortalso lands as a top-level policy field that toggles the default for every node, replacing the old server-config knob.A new
auto_update.enabledconfig option controls the tailnet-wide default for client auto-update. When true, every node's CapMap carriesdefault-auto-update: [true]so fresh clients pick up the default unless they make a local opt-in / opt-out choice.Policies that use the
funnelcap,ipPoolblocks, orautogroup:admin/autogroup:ownertargets are rejected at load — those features depend on machinery headscale does not yet ship.Taildrive
Taildrive (file-sync between nodes) is now configurable through policy. Grant
drive:shareto the node that hosts files anddrive:accessto nodes that read or write them; pair with atailscale.com/cap/drivegrant to set the per-share access mode:{ "nodeAttrs": [ { "target": ["tag:fileserver"], "attr": ["drive:share"] }, { "target": ["autogroup:member"], "attr": ["drive:access"] }, ], "grants": [ { "src": ["autogroup:member"], "dst": ["tag:fileserver"], "app": { "tailscale.com/cap/drive": [{ "shares": ["*"], "access": "rw" }], }, }, ], }A wildcard
nodeAttrs("target": ["*"]) hands the caps to every node when fine-grained control is not needed.Hostname sanitisation
Hostnames are now santised using Tailscales
magicdnssanitisation rules, matching Tailscale SaaS behavior. This means that hostnames with non-ASCII characters, special characters, or reserved DNS label characters are now transformed into valid DNS labels for MagicDNS. This improves our previously too strict sanitisation that rejected hostnames based on our guesswork and not based on the Tailscale upstream behaviour.Examples that previously regressed and now work:
Input Raw (Hostname) DNS label (GivenName) Joe's Mac miniJoe's Mac minijoes-mac-miniYuri's MacBook ProYuri's MacBook Proyuris-macbook-proTest@HostTest@Hosttest-hostmail.servermail.servermail-serverMy-PC!My-PC!my-pc我的电脑我的电脑nodeHA subnet router health probing
Headscale now actively probes HA subnet routers to detect nodes that are connected but not forwarding traffic. The control plane periodically pings HA subnet routers via the Noise control channel and fails over to a healthy standby if the primary stops responding. This is enabled by default (
node.routes.ha.probe_interval: 10s,probe_timeout: 5s) and only active when HA routes exist (2+ nodes advertising the same prefix). Setprobe_intervalto0to disable. This complements the existing disconnect-based failover, catching "zombie connected" routers that maintain their control session but cannot route packets. #3194BREAKING
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-<rand>. 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*
- This better matches Tailscale's security model where
- 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
- Policies using tags, hosts, or IP addresses as sources for
- 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
- Previously,
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-levelrandomizeClientPortfield, matching the Tailscale-hosted schema. #3251 Headscale refuses to start when the old key is set. Move it to the policy file referenced bypolicy.path:{ "randomizeClientPort": true, }If you do not have a policy file yet, create one with that minimal content and point
policy.pathat it. The default carries over — empty / absent policy meansrandomizeClientPort: false, matching the previous behaviour for operators who never set the key. Per-node opt-in vianodeAttrsis also supported and stacks on top of the global default.
CLI
headscale nodes registeris deprecated in favour ofheadscale auth register --auth-id <id> --user <user>#1850- The old command continues to work but will be removed in a future release
Changes
ACL Policy
- Fix subnet-to-subnet peer visibility — subnet routers now correctly become peers when ACL rules reference only subnet CIDRs as sources, without requiring node IP rules #3175
- Fix filter rule reduction to use only approved subnet routes instead of all advertised routes, matching Tailscale SaaS behavior #3175
- Add ICMP and IPv6-ICMP protocols to default filter rules when no protocol is specified #3036
- Fix autogroup:self handling for tagged nodes - tagged nodes no longer incorrectly receive autogroup:self filter rules #3036
- Use CIDR format for autogroup:self destination IPs matching Tailscale behavior #3036
- Merge filter rules with identical SrcIPs and IPProto matching Tailscale behavior - multiple ACL rules with the same source now produce a single FilterRule with combined DstPorts #3036
- Fix exit nodes incorrectly receiving filter rules for destinations that only overlap via exit routes #3169 #3175
- Fix address-based aliases (hosts, raw IPs) incorrectly expanding to include the matching node's other address family #2180
- Fix identity-based aliases (tags, users, groups) resolving to IPv4 only; they now include both IPv4 and IPv6 matching Tailscale behavior #2180
- Fix wildcard (
*) source in ACLs now using actually-approved subnet routes instead of autoApprover policy prefixes #2180 - Fix non-wildcard source IPs being dropped when combined with wildcard
*in the same ACL rule #2180 - Fix exit node approval not triggering filter rule recalculation for peers #2180
- Policy validation error messages now include field context (e.g.,
src=,dst=) and are more descriptive #2180 - Reject policies whose
user@tokens match multiple DB users; rename the duplicate viaheadscale users renameto load #3160 - Evaluate the policy
testsblock on user-initiated writes across bothaclsandgrants; reject policies whose tests fail (beta) #1803
Grants
- Add support for policy grants with
ip,app, andviafields #2180 - Add
autogroup:danger-allas a source-only autogroup resolving to all IP addresses #2180 - Add capability grants for Taildrive (
cap/drive) and peer relay (cap/relay) with automatic companion capabilities #2180 - Add per-viewer via route steering — grants with
viatags control which subnet router or exit node handles traffic for each group of viewers #2180 - Enable Taildrive node attributes on all nodes; actual access is controlled by
cap/drivegrants #2180
SSH Policy
- Add support for
localpart:*@<domain>in SSH ruleusersfield, mapping each matching user's email local-part as their OS username #3091 - Add SSH
checkaction support with OIDC and CLI-based approval flows #1850
CLI
- Add
headscale auth register,headscale auth approve, andheadscale auth rejectCLI commands #1850 - Deprecate
headscale nodes register --keyin favour ofheadscale auth register --auth-id#1850 headscale policy check --bypass-grpc-and-access-database-directlyvalidatesuser@tokens against the live user database #3160- Remove deprecated
--namespaceflag fromnodes list,nodes register, anddebug create-nodecommands (use--userinstead) #3093 - Remove deprecated
namespace/nscommand aliases forusersandmachine/machinesaliases fornodes#3093 - Fix
DestroyUserdeleting all pre-auth keys in the database instead of only the target user's keys #3155 headscale policy checkevaluates thetestsblock when invoked with--bypass-grpc-and-access-database-directly; without the flag it warns instead of running the tests against empty data #1803
API
- Add
authrelated routes. Theauth/registerendpoint now expects data as JSON #1850 - Remove gRPC reflection from the remote (TCP) server #3180
OIDC
- Add a confirmation page before completing node registration, showing the device hostname and machine key fingerprint #3180
- Generalise auth templates into reusable
AuthSuccessandAuthWebcomponents #1850 - Unify auth pipeline with
AuthVerdicttype, supporting registration, reauthentication, and SSH checks #1850
Configuration
- Add
node.expiryconfiguration option to set a default node key expiry for nodes registered via auth key #3122- Tagged nodes (registered with tagged pre-auth keys) are exempt from default expiry
oidc.expiryhas been removed; usenode.expiryinstead (applies to all registration methods including OIDC)ephemeral_node_inactivity_timeoutis deprecated in favour ofnode.ephemeral.inactivity_timeout
- Add
trusted_proxiesto gateTrue-Client-IP/X-Real-IP/X-Forwarded-For(previously honoured from any client) #3268
Debug
- Add node connectivity ping page for verifying control-plane reachability #3183
- Omit secret fields (
Pass,ClientSecret,APIKey) from/debug/configJSON output #3180 - Route
statsvizthroughtsweb.Protected#3180
Other
- Remove old migrations for the debian package #3185
- Install
config-example.yamlas example for the debian package #3186 - Fix user-owned re-registration with zero client expiry and no default storing
0001-01-01 00:00:00in the database instead ofNULL#3199 - Fix
tailscaledrestart on a node with no expiry resettingNULLto0001-01-01 00:00:00in the database, affecting both tagged and untagged nodes #3197 - Backfill
nodes.expiryrows persisted by older versions as0001-01-01 00:00:00toNULL, so nodes upgraded from <0.28 stop reporting as expired #3284 - Update reverse proxy documentation for
trusted_proxiesconfiguration option #3292
-
v0.29.0-beta.415 Jun 2026 pre-releaseRelease notes
Open source →Minimum supported Tailscale client version: v1.80.0
Tailscale ACL compatibility improvements
Extensive test cases were systematically generated using Tailscale clients and the official SaaS
to understand how the packet filter should be generated. We discovered a few differences, but
overall our implementation was very close.
#3036SSH check action
SSH rules with
"action": "check"are now supported. When a client initiates a SSH connection to a node
with acheckaction policy, the user is prompted to authenticate via OIDC or CLI approval before access
is granted. OIDC approval requires the authenticated user to own the source node; tagged source nodes
cannot use SSH check-mode.A new
headscale authCLI command group supports the approval flow:headscale auth approve --auth-id <id>approves a pending authentication request (SSH check or web auth)headscale auth reject --auth-id <id>rejects a pending authentication requestheadscale auth register --auth-id <id> --user <user>registers a node (replaces deprecatedheadscale nodes register)
Policy tests (beta)
Headscale now evaluates the
testsblock in a policy file. Tests assert reachability between
named sources and destinations and cover the whole policy — bothaclsandgrantsrules
contribute. They run on user-initiated writes viaheadscale policy set, on SIGHUP reload
(systemctl reload headscale/kill -HUP $(pidof headscale)), and onheadscale policy check.
A failing test rejects the write before it is applied, with the same error message Tailscale SaaS
would return for the same policy.At boot a stored policy whose tests no longer pass — for example because a referenced user was
deleted while the server was offline — logs a warning and the server keeps running. Fix the
policy and reload.This feature is beta while behavioural coverage against Tailscale SaaS broadens.
SSH policy tests (beta)
Headscale now evaluates the
sshTestsblock in a policy file. Each entry names a source, one or
more destination hosts, and three optional user lists:acceptasserts the listed login users
reach every destination via an accept- or check-action SSH rule,denyasserts none of them
reach any destination, andcheckrequires reachability specifically through a check-action
rule. Tests run onheadscale policy set, on SIGHUP reload (systemctl reload headscale/
kill -HUP $(pidof headscale)), and onheadscale policy check. A failing test rejects the
write before it is applied, with the same error message Tailscale SaaS would return for the same
policy.At boot a stored policy whose sshTests no longer pass — for example because a referenced user was
deleted while the server was offline — logs a warning and the server keeps running. Fix the
policy and reload.This feature is beta while behavioural coverage against Tailscale SaaS broadens.
SSH rule validation
SSH rule parsing now trims surrounding whitespace on
action,users,src, anddst,
rejects empty or wildcard entries inusers, rejects emptyacceptEnv, and rejects negative
checkPeriod.hosts:aliases are rejected as SSH destinations, non-ASCII tag names are
rejected at parse time, and the wording for group-nesting cycles matches Tailscale SaaS.
#3263Grants
We now support Tailscale grants
alongside ACLs. Grants extend what you can express in a policy beyond packet filtering: theapp
field controls application-level features like Taildrive file sharing and peer relay, and thevia
field steers traffic through specific tagged subnet routers or exit nodes. Theipfield works like
an ACL rule. Grants can be mixed with ACLs in the same policy file.
#2180As part of this, we added
autogroup:danger-all. It resolves to0.0.0.0/0and::/0, all IP
addresses, including those outside the tailnet. This replaces the old behaviour where*matched
all IPs (see BREAKING below). The name is intentional: accepting traffic from the entire
internet is a security-sensitive choice.autogroup:danger-allcan only be used as a source.Node attributes (
nodeAttrs)ACL policies now accept a
nodeAttrsblock. Each entry hands a list of
Tailscale node capabilities to every node matchingtarget. The accepted
target forms are the same asacls.srcandgrants.src: users, groups,
tags, hosts, prefixes,autogroup:member,autogroup:tagged, and*.{ "randomizeClientPort": true, "nodeAttrs": [ { "target": ["autogroup:tagged"], "attr": ["disable-captive-portal-detection"] }, { "target": ["[email protected]"], "attr": ["nextdns:abc123"] }, ], }Frequently requested capabilities this unlocks include
magicdns-aaaa,
disable-relay-server,disable-captive-portal-detection,
nextdns:<profile>/nextdns:no-device-info,randomize-client-port,
and the Taildrivedrive:share/drive:accesspair. The set is not
limited to these, any string-only cap an operator places in policy
reaches clients unchanged.randomizeClientPortalso lands as a top-level policy field that toggles
the default for every node, replacing the old server-config knob.A new
auto_update.enabledconfig option controls the tailnet-wide
default for client auto-update. When true, every node's CapMap carries
default-auto-update: [true]so fresh clients pick up the default
unless they make a local opt-in / opt-out choice.Policies that use the
funnelcap,ipPoolblocks, or
autogroup:admin/autogroup:ownertargets are rejected at load —
those features depend on machinery headscale does not yet ship.Taildrive
Taildrive (file-sync between
nodes) is now
configurable through policy. Grantdrive:shareto the node that
hosts files anddrive:accessto nodes that read or write them; pair
with atailscale.com/cap/drivegrant to set the per-share access
mode:{ "nodeAttrs": [ { "target": ["tag:fileserver"], "attr": ["drive:share"] }, { "target": ["autogroup:member"], "attr": ["drive:access"] }, ], "grants": [ { "src": ["autogroup:member"], "dst": ["tag:fileserver"], "app": { "tailscale.com/cap/drive": [{ "shares": ["*"], "access": "rw" }], }, }, ], }A wildcard
nodeAttrs("target": ["*"]) hands the caps to every
node when fine-grained control is not needed.Hostname sanitisation
Hostnames are now santised using Tailscales
magicdnssanitisation rules, matching Tailscale SaaS behavior. This means that hostnames with non-ASCII characters, special characters, or reserved DNS label characters are now transformed into valid DNS labels for MagicDNS. This improves our previously too strict sanitisation that rejected hostnames based on our guesswork and not based on the Tailscale upstream behaviour.Examples that previously regressed and now work:
Input Raw (Hostname) DNS label (GivenName) Joe's Mac miniJoe's Mac minijoes-mac-miniYuri's MacBook ProYuri's MacBook Proyuris-macbook-proTest@HostTest@Hosttest-hostmail.servermail.servermail-serverMy-PC!My-PC!my-pc我的电脑我的电脑nodeHA subnet router health probing
Headscale now actively probes HA subnet routers to detect nodes that are connected but not
forwarding traffic. The control plane periodically pings HA subnet routers via the Noise
control channel and fails over to a healthy standby if the primary stops responding. This is
enabled by default (node.routes.ha.probe_interval: 10s,probe_timeout: 5s) and only
active when HA routes exist (2+ nodes advertising the same prefix). Setprobe_intervalto
0to disable. This complements the existing disconnect-based failover, catching "zombie
connected" routers that maintain their control session but cannot route packets.
#3194BREAKING
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-<rand>. 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*
- This better matches Tailscale's security model where
- 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
- Policies using tags, hosts, or IP addresses as sources for
- 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
- Previously,
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
Headscale refuses to start when the old key is set. Move it to the
policy file referenced bypolicy.path:{ "randomizeClientPort": true, }If you do not have a policy file yet, create one with that minimal
content and pointpolicy.pathat it. The default carries over —
empty / absent policy meansrandomizeClientPort: false, matching
the previous behaviour for operators who never set the key. Per-node
opt-in vianodeAttrsis also supported and stacks on top of the
global default.
CLI
headscale nodes registeris deprecated in favour ofheadscale auth register --auth-id <id> --user <user>#1850- The old command continues to work but will be removed in a future release
Changes
ACL Policy
- Fix subnet-to-subnet peer visibility — subnet routers now correctly become peers when ACL rules reference only subnet CIDRs as sources, without requiring node IP rules #3175
- Fix filter rule reduction to use only approved subnet routes instead of all advertised routes, matching Tailscale SaaS behavior #3175
- Add ICMP and IPv6-ICMP protocols to default filter rules when no protocol is specified #3036
- Fix autogroup:self handling for tagged nodes - tagged nodes no longer incorrectly receive autogroup:self filter rules #3036
- Use CIDR format for autogroup:self destination IPs matching Tailscale behavior #3036
- Merge filter rules with identical SrcIPs and IPProto matching Tailscale behavior - multiple ACL rules with the same source now produce a single FilterRule with combined DstPorts #3036
- Fix exit nodes incorrectly receiving filter rules for destinations that only overlap via exit routes #3169 #3175
- Fix address-based aliases (hosts, raw IPs) incorrectly expanding to include the matching node's other address family #2180
- Fix identity-based aliases (tags, users, groups) resolving to IPv4 only; they now include both IPv4 and IPv6 matching Tailscale behavior #2180
- Fix wildcard (
*) source in ACLs now using actually-approved subnet routes instead of autoApprover policy prefixes #2180 - Fix non-wildcard source IPs being dropped when combined with wildcard
*in the same ACL rule #2180 - Fix exit node approval not triggering filter rule recalculation for peers #2180
- Policy validation error messages now include field context (e.g.,
src=,dst=) and are more descriptive #2180 - Reject policies whose
user@tokens match multiple DB users; rename the duplicate viaheadscale users renameto load #3160 - Evaluate the policy
testsblock on user-initiated writes across bothaclsandgrants; reject policies whose tests fail (beta) #1803
Grants
- Add support for policy grants with
ip,app, andviafields #2180 - Add
autogroup:danger-allas a source-only autogroup resolving to all IP addresses #2180 - Add capability grants for Taildrive (
cap/drive) and peer relay (cap/relay) with automatic companion capabilities #2180 - Add per-viewer via route steering — grants with
viatags control which subnet router or exit node handles traffic for each group of viewers #2180 - Enable Taildrive node attributes on all nodes; actual access is controlled by
cap/drivegrants #2180
SSH Policy
- Add support for
localpart:*@<domain>in SSH ruleusersfield, mapping each matching user's email local-part as their OS username #3091 - Add SSH
checkaction support with OIDC and CLI-based approval flows #1850
CLI
- Add
headscale auth register,headscale auth approve, andheadscale auth rejectCLI commands #1850 - Deprecate
headscale nodes register --keyin favour ofheadscale auth register --auth-id#1850 headscale policy check --bypass-grpc-and-access-database-directlyvalidatesuser@tokens against the live user database #3160- Remove deprecated
--namespaceflag fromnodes list,nodes register, anddebug create-nodecommands (use--userinstead) #3093 - Remove deprecated
namespace/nscommand aliases forusersandmachine/machinesaliases fornodes#3093 - Fix
DestroyUserdeleting all pre-auth keys in the database instead of only the target user's keys #3155 headscale policy checkevaluates thetestsblock when invoked with--bypass-grpc-and-access-database-directly; without the flag it warns instead of running the tests against empty data #1803
API
- Add
authrelated routes. Theauth/registerendpoint now expects data as JSON #1850 - Remove gRPC reflection from the remote (TCP) server #3180
OIDC
- Add a confirmation page before completing node registration, showing the device hostname and machine key fingerprint #3180
- Generalise auth templates into reusable
AuthSuccessandAuthWebcomponents #1850 - Unify auth pipeline with
AuthVerdicttype, supporting registration, reauthentication, and SSH checks #1850
Configuration
- Add
node.expiryconfiguration option to set a default node key expiry for nodes registered via auth key #3122- Tagged nodes (registered with tagged pre-auth keys) are exempt from default expiry
oidc.expiryhas been removed; usenode.expiryinstead (applies to all registration methods including OIDC)ephemeral_node_inactivity_timeoutis deprecated in favour ofnode.ephemeral.inactivity_timeout
- Add
trusted_proxiesto gateTrue-Client-IP/X-Real-IP/X-Forwarded-For(previously honoured from any client) #3268
Debug
- Add node connectivity ping page for verifying control-plane reachability #3183
- Omit secret fields (
Pass,ClientSecret,APIKey) from/debug/configJSON output #3180 - Route
statsvizthroughtsweb.Protected#3180
Other
- Remove old migrations for the debian package #3185
- Install
config-example.yamlas example for the debian package #3186 - Fix user-owned re-registration with zero client expiry and no default storing
0001-01-01 00:00:00in the database instead ofNULL#3199 - Fix
tailscaledrestart on a node with no expiry resettingNULLto0001-01-01 00:00:00in the database, affecting both tagged and untagged nodes #3197 - Backfill
nodes.expiryrows persisted by older versions as0001-01-01 00:00:00toNULL, so nodes upgraded from <0.28 stop reporting as expired #3284 - Update reverse proxy documentation for
trusted_proxiesconfiguration option #3292
Upgrade
Please follow the steps outlined in the upgrade guide to update your existing Headscale installation.
Changelog
- e759d9f auth: re-validate key when an expired node re-registers
- 68a6d3c db: drop ambiguous machine-key getter, match precisely in test helper
- 9b89497 db: treat unknown pre-auth key as not found
- a1d3e98 state: allow key expiry to be set on tagged nodes
- fd08b8f state: make any-user machine-key lookup deterministic
- a5ef3af state: patch relogins and gate endpoint broadcasts
- 0961e79 state: re-register converted tagged nodes with reused key
- a73d38b state: reject re-registration claiming another node's key
- 1689478 state: return all nodes for a machine key, reject ambiguous ownership
- 96d2e6e state: roll back node store when re-registration write fails
- b83bf3f state: serialise registration per machine key
- bff216a state: update node in place on pre-auth-key re-registration
- 4da0692 types/change: add NodeKeyRotated for relogin peer patch
-
v0.29.0-beta.3.0.20260615100239-a5ef3aff15ab15 Jun 2026 pre-releaseNothing published for this version
-
v0.29.0-beta.311 Jun 2026 pre-releaseRelease notes
Open source →Minimum supported Tailscale client version: v1.80.0
Tailscale ACL compatibility improvements
Extensive test cases were systematically generated using Tailscale clients and the official SaaS
to understand how the packet filter should be generated. We discovered a few differences, but
overall our implementation was very close.
#3036SSH check action
SSH rules with
"action": "check"are now supported. When a client initiates a SSH connection to a node
with acheckaction policy, the user is prompted to authenticate via OIDC or CLI approval before access
is granted. OIDC approval requires the authenticated user to own the source node; tagged source nodes
cannot use SSH check-mode.A new
headscale authCLI command group supports the approval flow:headscale auth approve --auth-id <id>approves a pending authentication request (SSH check or web auth)headscale auth reject --auth-id <id>rejects a pending authentication requestheadscale auth register --auth-id <id> --user <user>registers a node (replaces deprecatedheadscale nodes register)
Policy tests (beta)
Headscale now evaluates the
testsblock in a policy file. Tests assert reachability between
named sources and destinations and cover the whole policy — bothaclsandgrantsrules
contribute. They run on user-initiated writes viaheadscale policy set, on SIGHUP reload
(systemctl reload headscale/kill -HUP $(pidof headscale)), and onheadscale policy check.
A failing test rejects the write before it is applied, with the same error message Tailscale SaaS
would return for the same policy.At boot a stored policy whose tests no longer pass — for example because a referenced user was
deleted while the server was offline — logs a warning and the server keeps running. Fix the
policy and reload.This feature is beta while behavioural coverage against Tailscale SaaS broadens.
SSH policy tests (beta)
Headscale now evaluates the
sshTestsblock in a policy file. Each entry names a source, one or
more destination hosts, and three optional user lists:acceptasserts the listed login users
reach every destination via an accept- or check-action SSH rule,denyasserts none of them
reach any destination, andcheckrequires reachability specifically through a check-action
rule. Tests run onheadscale policy set, on SIGHUP reload (systemctl reload headscale/
kill -HUP $(pidof headscale)), and onheadscale policy check. A failing test rejects the
write before it is applied, with the same error message Tailscale SaaS would return for the same
policy.At boot a stored policy whose sshTests no longer pass — for example because a referenced user was
deleted while the server was offline — logs a warning and the server keeps running. Fix the
policy and reload.This feature is beta while behavioural coverage against Tailscale SaaS broadens.
SSH rule validation
SSH rule parsing now trims surrounding whitespace on
action,users,src, anddst,
rejects empty or wildcard entries inusers, rejects emptyacceptEnv, and rejects negative
checkPeriod.hosts:aliases are rejected as SSH destinations, non-ASCII tag names are
rejected at parse time, and the wording for group-nesting cycles matches Tailscale SaaS.
#3263Grants
We now support Tailscale grants
alongside ACLs. Grants extend what you can express in a policy beyond packet filtering: theapp
field controls application-level features like Taildrive file sharing and peer relay, and thevia
field steers traffic through specific tagged subnet routers or exit nodes. Theipfield works like
an ACL rule. Grants can be mixed with ACLs in the same policy file.
#2180As part of this, we added
autogroup:danger-all. It resolves to0.0.0.0/0and::/0, all IP
addresses, including those outside the tailnet. This replaces the old behaviour where*matched
all IPs (see BREAKING below). The name is intentional: accepting traffic from the entire
internet is a security-sensitive choice.autogroup:danger-allcan only be used as a source.Node attributes (
nodeAttrs)ACL policies now accept a
nodeAttrsblock. Each entry hands a list of
Tailscale node capabilities to every node matchingtarget. The accepted
target forms are the same asacls.srcandgrants.src: users, groups,
tags, hosts, prefixes,autogroup:member,autogroup:tagged, and*.{ "randomizeClientPort": true, "nodeAttrs": [ { "target": ["autogroup:tagged"], "attr": ["disable-captive-portal-detection"] }, { "target": ["[email protected]"], "attr": ["nextdns:abc123"] }, ], }Frequently requested capabilities this unlocks include
magicdns-aaaa,
disable-relay-server,disable-captive-portal-detection,
nextdns:<profile>/nextdns:no-device-info,randomize-client-port,
and the Taildrivedrive:share/drive:accesspair. The set is not
limited to these, any string-only cap an operator places in policy
reaches clients unchanged.randomizeClientPortalso lands as a top-level policy field that toggles
the default for every node, replacing the old server-config knob.A new
auto_update.enabledconfig option controls the tailnet-wide
default for client auto-update. When true, every node's CapMap carries
default-auto-update: [true]so fresh clients pick up the default
unless they make a local opt-in / opt-out choice.Policies that use the
funnelcap,ipPoolblocks, or
autogroup:admin/autogroup:ownertargets are rejected at load —
those features depend on machinery headscale does not yet ship.Taildrive
Taildrive (file-sync between
nodes) is now
configurable through policy. Grantdrive:shareto the node that
hosts files anddrive:accessto nodes that read or write them; pair
with atailscale.com/cap/drivegrant to set the per-share access
mode:{ "nodeAttrs": [ { "target": ["tag:fileserver"], "attr": ["drive:share"] }, { "target": ["autogroup:member"], "attr": ["drive:access"] }, ], "grants": [ { "src": ["autogroup:member"], "dst": ["tag:fileserver"], "app": { "tailscale.com/cap/drive": [{ "shares": ["*"], "access": "rw" }], }, }, ], }A wildcard
nodeAttrs("target": ["*"]) hands the caps to every
node when fine-grained control is not needed.Hostname sanitisation
Hostnames are now santised using Tailscales
magicdnssanitisation rules, matching Tailscale SaaS behavior. This means that hostnames with non-ASCII characters, special characters, or reserved DNS label characters are now transformed into valid DNS labels for MagicDNS. This improves our previously too strict sanitisation that rejected hostnames based on our guesswork and not based on the Tailscale upstream behaviour.Examples that previously regressed and now work:
Input Raw (Hostname) DNS label (GivenName) Joe's Mac miniJoe's Mac minijoes-mac-miniYuri's MacBook ProYuri's MacBook Proyuris-macbook-proTest@HostTest@Hosttest-hostmail.servermail.servermail-serverMy-PC!My-PC!my-pc我的电脑我的电脑nodeHA subnet router health probing
Headscale now actively probes HA subnet routers to detect nodes that are connected but not
forwarding traffic. The control plane periodically pings HA subnet routers via the Noise
control channel and fails over to a healthy standby if the primary stops responding. This is
enabled by default (node.routes.ha.probe_interval: 10s,probe_timeout: 5s) and only
active when HA routes exist (2+ nodes advertising the same prefix). Setprobe_intervalto
0to disable. This complements the existing disconnect-based failover, catching "zombie
connected" routers that maintain their control session but cannot route packets.
#3194BREAKING
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-<rand>. 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*
- This better matches Tailscale's security model where
- 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
- Policies using tags, hosts, or IP addresses as sources for
- 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
- Previously,
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
Headscale refuses to start when the old key is set. Move it to the
policy file referenced bypolicy.path:{ "randomizeClientPort": true, }If you do not have a policy file yet, create one with that minimal
content and pointpolicy.pathat it. The default carries over —
empty / absent policy meansrandomizeClientPort: false, matching
the previous behaviour for operators who never set the key. Per-node
opt-in vianodeAttrsis also supported and stacks on top of the
global default.
CLI
headscale nodes registeris deprecated in favour ofheadscale auth register --auth-id <id> --user <user>#1850- The old command continues to work but will be removed in a future release
Changes
ACL Policy
- Fix subnet-to-subnet peer visibility — subnet routers now correctly become peers when ACL rules reference only subnet CIDRs as sources, without requiring node IP rules #3175
- Fix filter rule reduction to use only approved subnet routes instead of all advertised routes, matching Tailscale SaaS behavior #3175
- Add ICMP and IPv6-ICMP protocols to default filter rules when no protocol is specified #3036
- Fix autogroup:self handling for tagged nodes - tagged nodes no longer incorrectly receive autogroup:self filter rules #3036
- Use CIDR format for autogroup:self destination IPs matching Tailscale behavior #3036
- Merge filter rules with identical SrcIPs and IPProto matching Tailscale behavior - multiple ACL rules with the same source now produce a single FilterRule with combined DstPorts #3036
- Fix exit nodes incorrectly receiving filter rules for destinations that only overlap via exit routes #3169 #3175
- Fix address-based aliases (hosts, raw IPs) incorrectly expanding to include the matching node's other address family #2180
- Fix identity-based aliases (tags, users, groups) resolving to IPv4 only; they now include both IPv4 and IPv6 matching Tailscale behavior #2180
- Fix wildcard (
*) source in ACLs now using actually-approved subnet routes instead of autoApprover policy prefixes #2180 - Fix non-wildcard source IPs being dropped when combined with wildcard
*in the same ACL rule #2180 - Fix exit node approval not triggering filter rule recalculation for peers #2180
- Policy validation error messages now include field context (e.g.,
src=,dst=) and are more descriptive #2180 - Reject policies whose
user@tokens match multiple DB users; rename the duplicate viaheadscale users renameto load #3160 - Evaluate the policy
testsblock on user-initiated writes across bothaclsandgrants; reject policies whose tests fail (beta) #1803
Grants
- Add support for policy grants with
ip,app, andviafields #2180 - Add
autogroup:danger-allas a source-only autogroup resolving to all IP addresses #2180 - Add capability grants for Taildrive (
cap/drive) and peer relay (cap/relay) with automatic companion capabilities #2180 - Add per-viewer via route steering — grants with
viatags control which subnet router or exit node handles traffic for each group of viewers #2180 - Enable Taildrive node attributes on all nodes; actual access is controlled by
cap/drivegrants #2180
SSH Policy
- Add support for
localpart:*@<domain>in SSH ruleusersfield, mapping each matching user's email local-part as their OS username #3091 - Add SSH
checkaction support with OIDC and CLI-based approval flows #1850
CLI
- Add
headscale auth register,headscale auth approve, andheadscale auth rejectCLI commands #1850 - Deprecate
headscale nodes register --keyin favour ofheadscale auth register --auth-id#1850 headscale policy check --bypass-grpc-and-access-database-directlyvalidatesuser@tokens against the live user database #3160- Remove deprecated
--namespaceflag fromnodes list,nodes register, anddebug create-nodecommands (use--userinstead) #3093 - Remove deprecated
namespace/nscommand aliases forusersandmachine/machinesaliases fornodes#3093 - Fix
DestroyUserdeleting all pre-auth keys in the database instead of only the target user's keys #3155 headscale policy checkevaluates thetestsblock when invoked with--bypass-grpc-and-access-database-directly; without the flag it warns instead of running the tests against empty data #1803
API
- Add
authrelated routes. Theauth/registerendpoint now expects data as JSON #1850 - Remove gRPC reflection from the remote (TCP) server #3180
OIDC
- Add a confirmation page before completing node registration, showing the device hostname and machine key fingerprint #3180
- Generalise auth templates into reusable
AuthSuccessandAuthWebcomponents #1850 - Unify auth pipeline with
AuthVerdicttype, supporting registration, reauthentication, and SSH checks #1850
Configuration
- Add
node.expiryconfiguration option to set a default node key expiry for nodes registered via auth key #3122- Tagged nodes (registered with tagged pre-auth keys) are exempt from default expiry
oidc.expiryhas been removed; usenode.expiryinstead (applies to all registration methods including OIDC)ephemeral_node_inactivity_timeoutis deprecated in favour ofnode.ephemeral.inactivity_timeout
- Add
trusted_proxiesto gateTrue-Client-IP/X-Real-IP/X-Forwarded-For(previously honoured from any client) #3268
Debug
- Add node connectivity ping page for verifying control-plane reachability #3183
- Omit secret fields (
Pass,ClientSecret,APIKey) from/debug/configJSON output #3180 - Route
statsvizthroughtsweb.Protected#3180
Other
- Remove old migrations for the debian package #3185
- Install
config-example.yamlas example for the debian package #3186 - Fix user-owned re-registration with zero client expiry and no default storing
0001-01-01 00:00:00in the database instead ofNULL#3199 - Fix
tailscaledrestart on a node with no expiry resettingNULLto0001-01-01 00:00:00in the database, affecting both tagged and untagged nodes #3197 - Backfill
nodes.expiryrows persisted by older versions as0001-01-01 00:00:00toNULL, so nodes upgraded from <0.28 stop reporting as expired #3284 - Update reverse proxy documentation for
trusted_proxiesconfiguration option #3292
Upgrade
Please follow the steps outlined in the upgrade guide to update your existing Headscale installation.
Changelog
- 6777a82 Rewrite reverse proxy documentation
- c5f3d5c auth: clamp logout expiry to now
- 5228cb1 change: drop subnet-router full update, use policy change
- 7d845ef db: advance allocator cursor under lock during IP backfill
- f61753e db: bound IP allocation scan so exhausted prefixes error out
- ec94573 db: drop superseded ephemeral deletions in the GC drain loop
- 99ad555 db: handle degenerate prefixes in random IP allocation
- 10696fa db: look up API keys by explicit primary key, not struct condition
- 4c165ae db: reap ephemeral GC watcher goroutine on cancel and reschedule
- e413919 derp: clone regions when merging DERP maps
- 9f0c74e dns: release lock before extra-records channel send
- 8f75ee5 docker: head tailscale latest go
- f585f8a flake, go.mod: move to Go 1.26.4 and refresh dependencies
- 21058d1 flake, go.mod: refresh dependencies
- 29f87e5 flakehashes: refresh vendor hash after dropping gorilla/mux
- f61d21b go.mod: drop unused gorilla/mux dependency
- b892b8f hscontrol: read Apple platform via chi.URLParam, not mux.Vars
- 88044f4 hscontrol: satisfy golangci-lint on changed lines
- cffdb77 mapper, change: coalesce duplicate policy recomputes per tick
- 5e05652 mapper: derive incremental visibility from one shared filter
- 8237ac6 mapper: filter incremental UserProfiles by ACL visibility
- cd1c208 mapper: filter peer-change patches by ACL visibility
- f4eeb94 mapper: gate broadcast sends until a connection's initial map is delivered
- c483beb mapper: guard nil Hostinfo in addNextDNSMetadata
- efdd946 mapper: keep one batched bundle per node in flight to preserve order
- ad2693f mapper: record initial-map peers only after delivery
- 0e7b154 mapper: register reconnecting node atomically against cleanup
- fad8f2a mapper: test incremental visibility matches full map
- 9fc88e3 noise: drop write-only nodeKey field to fix data race
- 71a4ce3 noise: re-delegate SSH check when the auth session is missing (#3306)
- 0921972 oidc: avoid slice panic in getCookieName for short values
- 0fdff0c oidc: set SameSite=Lax on state/nonce CSRF cookies
- bceac49 policy: add NodeNeedsPeerRecompute predicate
- 56cd3eb policy: guard SSHCheckParams autogroup:self against nil User
- 2c9164b policy: precompute node routes in the peer-map build
- 40ed210 policy: read pm.pol under the mutex
- 020560f policy: remove unused top-level BuildPeerMap
- 7918187 servertest: add logout/relogin storm repro with poll churn
- f497b4e state, poll: refcount poll sessions, mark offline only on last release
- a518a50 state: batch route auto-approval into one policy rebuild
- 7706552 state: gate reconnect PolicyChange on NodeNeedsPeerRecompute
- 06d6816 state: keep nil expiry for nodes that stay tagged on reauth
- 2e24018 state: persist live NodeStore node in persistNodeToDB
- 4914f9f state: reject re-auth claiming another machine's NodeKey
- eb57a3a state: reject registration claiming another machine's NodeKey
- 017162d state: signal NodeStore shutdown without closing writeQueue
- 08f186f state: skip database persist for keepalive-only map requests
- 759381a types: add ActiveSessions poll session refcount to Node
- 4f67300 types: clone Hostinfo before applying DERP change
- 5a70a72 types: lock tailcfg DNS config access for extra-records updates
- bb06b90 types: skip malformed derp.urls entries instead of panicking
- 84c9902 util: check RNG error before slicing url-safe random string
- ba54349 util: handle single-address IPv4 prefix in reverse DNS generation
-
v0.29.0-beta.2.0.20260609132118-f585f8a94d5c09 Jun 2026 pre-releaseNothing published for this version
-
v0.29.0-beta.229 May 2026 pre-releaseRelease notes
Open source →Minimum supported Tailscale client version: v1.80.0
Tailscale ACL compatibility improvements
Extensive test cases were systematically generated using Tailscale clients and the official SaaS
to understand how the packet filter should be generated. We discovered a few differences, but
overall our implementation was very close.
#3036SSH check action
SSH rules with
"action": "check"are now supported. When a client initiates a SSH connection to a node
with acheckaction policy, the user is prompted to authenticate via OIDC or CLI approval before access
is granted. OIDC approval requires the authenticated user to own the source node; tagged source nodes
cannot use SSH check-mode.A new
headscale authCLI command group supports the approval flow:headscale auth approve --auth-id <id>approves a pending authentication request (SSH check or web auth)headscale auth reject --auth-id <id>rejects a pending authentication requestheadscale auth register --auth-id <id> --user <user>registers a node (replaces deprecatedheadscale nodes register)
Policy tests (beta)
Headscale now evaluates the
testsblock in a policy file. Tests assert reachability between
named sources and destinations and cover the whole policy — bothaclsandgrantsrules
contribute. They run on user-initiated writes viaheadscale policy set, on SIGHUP reload
(systemctl reload headscale/kill -HUP $(pidof headscale)), and onheadscale policy check.
A failing test rejects the write before it is applied, with the same error message Tailscale SaaS
would return for the same policy.At boot a stored policy whose tests no longer pass — for example because a referenced user was
deleted while the server was offline — logs a warning and the server keeps running. Fix the
policy and reload.This feature is beta while behavioural coverage against Tailscale SaaS broadens.
SSH policy tests (beta)
Headscale now evaluates the
sshTestsblock in a policy file. Each entry names a source, one or
more destination hosts, and three optional user lists:acceptasserts the listed login users
reach every destination via an accept- or check-action SSH rule,denyasserts none of them
reach any destination, andcheckrequires reachability specifically through a check-action
rule. Tests run onheadscale policy set, on SIGHUP reload (systemctl reload headscale/
kill -HUP $(pidof headscale)), and onheadscale policy check. A failing test rejects the
write before it is applied, with the same error message Tailscale SaaS would return for the same
policy.At boot a stored policy whose sshTests no longer pass — for example because a referenced user was
deleted while the server was offline — logs a warning and the server keeps running. Fix the
policy and reload.This feature is beta while behavioural coverage against Tailscale SaaS broadens.
SSH rule validation
SSH rule parsing now trims surrounding whitespace on
action,users,src, anddst,
rejects empty or wildcard entries inusers, rejects emptyacceptEnv, and rejects negative
checkPeriod.hosts:aliases are rejected as SSH destinations, non-ASCII tag names are
rejected at parse time, and the wording for group-nesting cycles matches Tailscale SaaS.
#3263Grants
We now support Tailscale grants
alongside ACLs. Grants extend what you can express in a policy beyond packet filtering: theapp
field controls application-level features like Taildrive file sharing and peer relay, and thevia
field steers traffic through specific tagged subnet routers or exit nodes. Theipfield works like
an ACL rule. Grants can be mixed with ACLs in the same policy file.
#2180As part of this, we added
autogroup:danger-all. It resolves to0.0.0.0/0and::/0, all IP
addresses, including those outside the tailnet. This replaces the old behaviour where*matched
all IPs (see BREAKING below). The name is intentional: accepting traffic from the entire
internet is a security-sensitive choice.autogroup:danger-allcan only be used as a source.Node attributes (
nodeAttrs)ACL policies now accept a
nodeAttrsblock. Each entry hands a list of
Tailscale node capabilities to every node matchingtarget. The accepted
target forms are the same asacls.srcandgrants.src: users, groups,
tags, hosts, prefixes,autogroup:member,autogroup:tagged, and*.{ "randomizeClientPort": true, "nodeAttrs": [ { "target": ["autogroup:tagged"], "attr": ["disable-captive-portal-detection"] }, { "target": ["[email protected]"], "attr": ["nextdns:abc123"] }, ], }Frequently requested capabilities this unlocks include
magicdns-aaaa,
disable-relay-server,disable-captive-portal-detection,
nextdns:<profile>/nextdns:no-device-info,randomize-client-port,
and the Taildrivedrive:share/drive:accesspair. The set is not
limited to these, any string-only cap an operator places in policy
reaches clients unchanged.randomizeClientPortalso lands as a top-level policy field that toggles
the default for every node, replacing the old server-config knob.A new
auto_update.enabledconfig option controls the tailnet-wide
default for client auto-update. When true, every node's CapMap carries
default-auto-update: [true]so fresh clients pick up the default
unless they make a local opt-in / opt-out choice.Policies that use the
funnelcap,ipPoolblocks, or
autogroup:admin/autogroup:ownertargets are rejected at load —
those features depend on machinery headscale does not yet ship.Taildrive
Taildrive (file-sync between
nodes) is now
configurable through policy. Grantdrive:shareto the node that
hosts files anddrive:accessto nodes that read or write them; pair
with atailscale.com/cap/drivegrant to set the per-share access
mode:{ "nodeAttrs": [ { "target": ["tag:fileserver"], "attr": ["drive:share"] }, { "target": ["autogroup:member"], "attr": ["drive:access"] }, ], "grants": [ { "src": ["autogroup:member"], "dst": ["tag:fileserver"], "app": { "tailscale.com/cap/drive": [{ "shares": ["*"], "access": "rw" }], }, }, ], }A wildcard
nodeAttrs("target": ["*"]) hands the caps to every
node when fine-grained control is not needed.Hostname sanitisation
Hostnames are now santised using Tailscales
magicdnssanitisation rules, matching Tailscale SaaS behavior. This means that hostnames with non-ASCII characters, special characters, or reserved DNS label characters are now transformed into valid DNS labels for MagicDNS. This improves our previously too strict sanitisation that rejected hostnames based on our guesswork and not based on the Tailscale upstream behaviour.Examples that previously regressed and now work:
Input Raw (Hostname) DNS label (GivenName) Joe's Mac miniJoe's Mac minijoes-mac-miniYuri's MacBook ProYuri's MacBook Proyuris-macbook-proTest@HostTest@Hosttest-hostmail.servermail.servermail-serverMy-PC!My-PC!my-pc我的电脑我的电脑nodeHA subnet router health probing
Headscale now actively probes HA subnet routers to detect nodes that are connected but not
forwarding traffic. The control plane periodically pings HA subnet routers via the Noise
control channel and fails over to a healthy standby if the primary stops responding. This is
enabled by default (node.routes.ha.probe_interval: 10s,probe_timeout: 5s) and only
active when HA routes exist (2+ nodes advertising the same prefix). Setprobe_intervalto
0to disable. This complements the existing disconnect-based failover, catching "zombie
connected" routers that maintain their control session but cannot route packets.
#3194BREAKING
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-<rand>. 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*
- This better matches Tailscale's security model where
- 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
- Policies using tags, hosts, or IP addresses as sources for
- 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
- Previously,
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
Headscale refuses to start when the old key is set. Move it to the
policy file referenced bypolicy.path:{ "randomizeClientPort": true, }If you do not have a policy file yet, create one with that minimal
content and pointpolicy.pathat it. The default carries over —
empty / absent policy meansrandomizeClientPort: false, matching
the previous behaviour for operators who never set the key. Per-node
opt-in vianodeAttrsis also supported and stacks on top of the
global default.
CLI
headscale nodes registeris deprecated in favour ofheadscale auth register --auth-id <id> --user <user>#1850- The old command continues to work but will be removed in a future release
Changes
ACL Policy
- Fix subnet-to-subnet peer visibility — subnet routers now correctly become peers when ACL rules reference only subnet CIDRs as sources, without requiring node IP rules #3175
- Fix filter rule reduction to use only approved subnet routes instead of all advertised routes, matching Tailscale SaaS behavior #3175
- Add ICMP and IPv6-ICMP protocols to default filter rules when no protocol is specified #3036
- Fix autogroup:self handling for tagged nodes - tagged nodes no longer incorrectly receive autogroup:self filter rules #3036
- Use CIDR format for autogroup:self destination IPs matching Tailscale behavior #3036
- Merge filter rules with identical SrcIPs and IPProto matching Tailscale behavior - multiple ACL rules with the same source now produce a single FilterRule with combined DstPorts #3036
- Fix exit nodes incorrectly receiving filter rules for destinations that only overlap via exit routes #3169 #3175
- Fix address-based aliases (hosts, raw IPs) incorrectly expanding to include the matching node's other address family #2180
- Fix identity-based aliases (tags, users, groups) resolving to IPv4 only; they now include both IPv4 and IPv6 matching Tailscale behavior #2180
- Fix wildcard (
*) source in ACLs now using actually-approved subnet routes instead of autoApprover policy prefixes #2180 - Fix non-wildcard source IPs being dropped when combined with wildcard
*in the same ACL rule #2180 - Fix exit node approval not triggering filter rule recalculation for peers #2180
- Policy validation error messages now include field context (e.g.,
src=,dst=) and are more descriptive #2180 - Reject policies whose
user@tokens match multiple DB users; rename the duplicate viaheadscale users renameto load #3160 - Evaluate the policy
testsblock on user-initiated writes across bothaclsandgrants; reject policies whose tests fail (beta) #1803
Grants
- Add support for policy grants with
ip,app, andviafields #2180 - Add
autogroup:danger-allas a source-only autogroup resolving to all IP addresses #2180 - Add capability grants for Taildrive (
cap/drive) and peer relay (cap/relay) with automatic companion capabilities #2180 - Add per-viewer via route steering — grants with
viatags control which subnet router or exit node handles traffic for each group of viewers #2180 - Enable Taildrive node attributes on all nodes; actual access is controlled by
cap/drivegrants #2180
SSH Policy
- Add support for
localpart:*@<domain>in SSH ruleusersfield, mapping each matching user's email local-part as their OS username #3091 - Add SSH
checkaction support with OIDC and CLI-based approval flows #1850
CLI
- Add
headscale auth register,headscale auth approve, andheadscale auth rejectCLI commands #1850 - Deprecate
headscale nodes register --keyin favour ofheadscale auth register --auth-id#1850 headscale policy check --bypass-grpc-and-access-database-directlyvalidatesuser@tokens against the live user database #3160- Remove deprecated
--namespaceflag fromnodes list,nodes register, anddebug create-nodecommands (use--userinstead) #3093 - Remove deprecated
namespace/nscommand aliases forusersandmachine/machinesaliases fornodes#3093 - Fix
DestroyUserdeleting all pre-auth keys in the database instead of only the target user's keys #3155 headscale policy checkevaluates thetestsblock when invoked with--bypass-grpc-and-access-database-directly; without the flag it warns instead of running the tests against empty data #1803
API
- Add
authrelated routes. Theauth/registerendpoint now expects data as JSON #1850 - Remove gRPC reflection from the remote (TCP) server #3180
OIDC
- Add a confirmation page before completing node registration, showing the device hostname and machine key fingerprint #3180
- Generalise auth templates into reusable
AuthSuccessandAuthWebcomponents #1850 - Unify auth pipeline with
AuthVerdicttype, supporting registration, reauthentication, and SSH checks #1850
Configuration
- Add
node.expiryconfiguration option to set a default node key expiry for nodes registered via auth key #3122- Tagged nodes (registered with tagged pre-auth keys) are exempt from default expiry
oidc.expiryhas been removed; usenode.expiryinstead (applies to all registration methods including OIDC)ephemeral_node_inactivity_timeoutis deprecated in favour ofnode.ephemeral.inactivity_timeout
- Add
trusted_proxiesto gateTrue-Client-IP/X-Real-IP/X-Forwarded-For(previously honoured from any client) #3268
Debug
- Add node connectivity ping page for verifying control-plane reachability #3183
- Omit secret fields (
Pass,ClientSecret,APIKey) from/debug/configJSON output #3180 - Route
statsvizthroughtsweb.Protected#3180
Other
- Remove old migrations for the debian package #3185
- Install
config-example.yamlas example for the debian package #3186 - Fix user-owned re-registration with zero client expiry and no default storing
0001-01-01 00:00:00in the database instead ofNULL#3199- Pre-existing rows with
0001-01-01 00:00:00are not backfilled; they clear themselves the next time the node re-registers
- Pre-existing rows with
- Fix
tailscaledrestart on a node with no expiry resettingNULLto0001-01-01 00:00:00in the database, affecting both tagged and untagged nodes #3197
Upgrade
Please follow the steps outlined in the upgrade guide to update your existing Headscale installation.
Changelog
- ea8fc72 db: backfill zero-time node expiry to NULL
- 77ba225 db: treat Go module pseudo-versions as dev builds
- 66a5f99 gh: pre-pull released tailscale images for fork-PR CI
- 79562b9 hi: add list-versions subcommand
- 171fd7a policy: key autogroup:self invalidation on UserID not User view
- 4483fd0 tsic, gh: keep unstable on Docker Hub
- 2e49f3d tsic: pull tailscale images from ghcr.io
-
v0.29.0-beta.1.0.20260527074905-ea8fc725700927 May 2026 pre-releaseNothing published for this version
-
v0.29.0-beta.1.0.20260522122924-4483fd0cad3822 May 2026 pre-releaseNothing published for this version
-
v0.29.0-beta.122 May 2026 pre-releaseRelease notes
Open source →Minimum supported Tailscale client version: v1.80.0
Tailscale ACL compatibility improvements
Extensive test cases were systematically generated using Tailscale clients and the official SaaS
to understand how the packet filter should be generated. We discovered a few differences, but
overall our implementation was very close.
#3036SSH check action
SSH rules with
"action": "check"are now supported. When a client initiates a SSH connection to a node
with acheckaction policy, the user is prompted to authenticate via OIDC or CLI approval before access
is granted. OIDC approval requires the authenticated user to own the source node; tagged source nodes
cannot use SSH check-mode.A new
headscale authCLI command group supports the approval flow:headscale auth approve --auth-id <id>approves a pending authentication request (SSH check or web auth)headscale auth reject --auth-id <id>rejects a pending authentication requestheadscale auth register --auth-id <id> --user <user>registers a node (replaces deprecatedheadscale nodes register)
Policy tests (beta)
Headscale now evaluates the
testsblock in a policy file. Tests assert reachability between
named sources and destinations and cover the whole policy — bothaclsandgrantsrules
contribute. They run on user-initiated writes viaheadscale policy set, on SIGHUP reload
(systemctl reload headscale/kill -HUP $(pidof headscale)), and onheadscale policy check.
A failing test rejects the write before it is applied, with the same error message Tailscale SaaS
would return for the same policy.At boot a stored policy whose tests no longer pass — for example because a referenced user was
deleted while the server was offline — logs a warning and the server keeps running. Fix the
policy and reload.This feature is beta while behavioural coverage against Tailscale SaaS broadens.
SSH policy tests (beta)
Headscale now evaluates the
sshTestsblock in a policy file. Each entry names a source, one or
more destination hosts, and three optional user lists:acceptasserts the listed login users
reach every destination via an accept- or check-action SSH rule,denyasserts none of them
reach any destination, andcheckrequires reachability specifically through a check-action
rule. Tests run onheadscale policy set, on SIGHUP reload (systemctl reload headscale/
kill -HUP $(pidof headscale)), and onheadscale policy check. A failing test rejects the
write before it is applied, with the same error message Tailscale SaaS would return for the same
policy.At boot a stored policy whose sshTests no longer pass — for example because a referenced user was
deleted while the server was offline — logs a warning and the server keeps running. Fix the
policy and reload.This feature is beta while behavioural coverage against Tailscale SaaS broadens.
SSH rule validation
SSH rule parsing now trims surrounding whitespace on
action,users,src, anddst,
rejects empty or wildcard entries inusers, rejects emptyacceptEnv, and rejects negative
checkPeriod.hosts:aliases are rejected as SSH destinations, non-ASCII tag names are
rejected at parse time, and the wording for group-nesting cycles matches Tailscale SaaS.
#3263Grants
We now support Tailscale grants
alongside ACLs. Grants extend what you can express in a policy beyond packet filtering: theapp
field controls application-level features like Taildrive file sharing and peer relay, and thevia
field steers traffic through specific tagged subnet routers or exit nodes. Theipfield works like
an ACL rule. Grants can be mixed with ACLs in the same policy file.
#2180As part of this, we added
autogroup:danger-all. It resolves to0.0.0.0/0and::/0, all IP
addresses, including those outside the tailnet. This replaces the old behaviour where*matched
all IPs (see BREAKING below). The name is intentional: accepting traffic from the entire
internet is a security-sensitive choice.autogroup:danger-allcan only be used as a source.Node attributes (
nodeAttrs)ACL policies now accept a
nodeAttrsblock. Each entry hands a list of
Tailscale node capabilities to every node matchingtarget. The accepted
target forms are the same asacls.srcandgrants.src: users, groups,
tags, hosts, prefixes,autogroup:member,autogroup:tagged, and*.{ "randomizeClientPort": true, "nodeAttrs": [ { "target": ["autogroup:tagged"], "attr": ["disable-captive-portal-detection"] }, { "target": ["[email protected]"], "attr": ["nextdns:abc123"] }, ], }Frequently requested capabilities this unlocks include
magicdns-aaaa,
disable-relay-server,disable-captive-portal-detection,
nextdns:<profile>/nextdns:no-device-info,randomize-client-port,
and the Taildrivedrive:share/drive:accesspair. The set is not
limited to these, any string-only cap an operator places in policy
reaches clients unchanged.randomizeClientPortalso lands as a top-level policy field that toggles
the default for every node, replacing the old server-config knob.A new
auto_update.enabledconfig option controls the tailnet-wide
default for client auto-update. When true, every node's CapMap carries
default-auto-update: [true]so fresh clients pick up the default
unless they make a local opt-in / opt-out choice.Policies that use the
funnelcap,ipPoolblocks, or
autogroup:admin/autogroup:ownertargets are rejected at load —
those features depend on machinery headscale does not yet ship.Taildrive
Taildrive (file-sync between
nodes) is now
configurable through policy. Grantdrive:shareto the node that
hosts files anddrive:accessto nodes that read or write them; pair
with atailscale.com/cap/drivegrant to set the per-share access
mode:{ "nodeAttrs": [ { "target": ["tag:fileserver"], "attr": ["drive:share"] }, { "target": ["autogroup:member"], "attr": ["drive:access"] }, ], "grants": [ { "src": ["autogroup:member"], "dst": ["tag:fileserver"], "app": { "tailscale.com/cap/drive": [{ "shares": ["*"], "access": "rw" }], }, }, ], }A wildcard
nodeAttrs("target": ["*"]) hands the caps to every
node when fine-grained control is not needed.Hostname sanitisation
Hostnames are now santised using Tailscales
magicdnssanitisation rules, matching Tailscale SaaS behavior. This means that hostnames with non-ASCII characters, special characters, or reserved DNS label characters are now transformed into valid DNS labels for MagicDNS. This improves our previously too strict sanitisation that rejected hostnames based on our guesswork and not based on the Tailscale upstream behaviour.Examples that previously regressed and now work:
Input Raw (Hostname) DNS label (GivenName) Joe's Mac miniJoe's Mac minijoes-mac-miniYuri's MacBook ProYuri's MacBook Proyuris-macbook-proTest@HostTest@Hosttest-hostmail.servermail.servermail-serverMy-PC!My-PC!my-pc我的电脑我的电脑nodeHA subnet router health probing
Headscale now actively probes HA subnet routers to detect nodes that are connected but not
forwarding traffic. The control plane periodically pings HA subnet routers via the Noise
control channel and fails over to a healthy standby if the primary stops responding. This is
enabled by default (node.routes.ha.probe_interval: 10s,probe_timeout: 5s) and only
active when HA routes exist (2+ nodes advertising the same prefix). Setprobe_intervalto
0to disable. This complements the existing disconnect-based failover, catching "zombie
connected" routers that maintain their control session but cannot route packets.
#3194BREAKING
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-<rand>. 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*
- This better matches Tailscale's security model where
- 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
- Policies using tags, hosts, or IP addresses as sources for
- 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
- Previously,
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
Headscale refuses to start when the old key is set. Move it to the
policy file referenced bypolicy.path:{ "randomizeClientPort": true, }If you do not have a policy file yet, create one with that minimal
content and pointpolicy.pathat it. The default carries over —
empty / absent policy meansrandomizeClientPort: false, matching
the previous behaviour for operators who never set the key. Per-node
opt-in vianodeAttrsis also supported and stacks on top of the
global default.
CLI
headscale nodes registeris deprecated in favour ofheadscale auth register --auth-id <id> --user <user>#1850- The old command continues to work but will be removed in a future release
Changes
ACL Policy
- Fix subnet-to-subnet peer visibility — subnet routers now correctly become peers when ACL rules reference only subnet CIDRs as sources, without requiring node IP rules #3175
- Fix filter rule reduction to use only approved subnet routes instead of all advertised routes, matching Tailscale SaaS behavior #3175
- Add ICMP and IPv6-ICMP protocols to default filter rules when no protocol is specified #3036
- Fix autogroup:self handling for tagged nodes - tagged nodes no longer incorrectly receive autogroup:self filter rules #3036
- Use CIDR format for autogroup:self destination IPs matching Tailscale behavior #3036
- Merge filter rules with identical SrcIPs and IPProto matching Tailscale behavior - multiple ACL rules with the same source now produce a single FilterRule with combined DstPorts #3036
- Fix exit nodes incorrectly receiving filter rules for destinations that only overlap via exit routes #3169 #3175
- Fix address-based aliases (hosts, raw IPs) incorrectly expanding to include the matching node's other address family #2180
- Fix identity-based aliases (tags, users, groups) resolving to IPv4 only; they now include both IPv4 and IPv6 matching Tailscale behavior #2180
- Fix wildcard (
*) source in ACLs now using actually-approved subnet routes instead of autoApprover policy prefixes #2180 - Fix non-wildcard source IPs being dropped when combined with wildcard
*in the same ACL rule #2180 - Fix exit node approval not triggering filter rule recalculation for peers #2180
- Policy validation error messages now include field context (e.g.,
src=,dst=) and are more descriptive #2180 - Reject policies whose
user@tokens match multiple DB users; rename the duplicate viaheadscale users renameto load #3160 - Evaluate the policy
testsblock on user-initiated writes across bothaclsandgrants; reject policies whose tests fail (beta) #1803
Grants
- Add support for policy grants with
ip,app, andviafields #2180 - Add
autogroup:danger-allas a source-only autogroup resolving to all IP addresses #2180 - Add capability grants for Taildrive (
cap/drive) and peer relay (cap/relay) with automatic companion capabilities #2180 - Add per-viewer via route steering — grants with
viatags control which subnet router or exit node handles traffic for each group of viewers #2180 - Enable Taildrive node attributes on all nodes; actual access is controlled by
cap/drivegrants #2180
SSH Policy
- Add support for
localpart:*@<domain>in SSH ruleusersfield, mapping each matching user's email local-part as their OS username #3091 - Add SSH
checkaction support with OIDC and CLI-based approval flows #1850
CLI
- Add
headscale auth register,headscale auth approve, andheadscale auth rejectCLI commands #1850 - Deprecate
headscale nodes register --keyin favour ofheadscale auth register --auth-id#1850 headscale policy check --bypass-grpc-and-access-database-directlyvalidatesuser@tokens against the live user database #3160- Remove deprecated
--namespaceflag fromnodes list,nodes register, anddebug create-nodecommands (use--userinstead) #3093 - Remove deprecated
namespace/nscommand aliases forusersandmachine/machinesaliases fornodes#3093 - Fix
DestroyUserdeleting all pre-auth keys in the database instead of only the target user's keys #3155 headscale policy checkevaluates thetestsblock when invoked with--bypass-grpc-and-access-database-directly; without the flag it warns instead of running the tests against empty data #1803
API
- Add
authrelated routes. Theauth/registerendpoint now expects data as JSON #1850 - Remove gRPC reflection from the remote (TCP) server #3180
OIDC
- Add a confirmation page before completing node registration, showing the device hostname and machine key fingerprint #3180
- Generalise auth templates into reusable
AuthSuccessandAuthWebcomponents #1850 - Unify auth pipeline with
AuthVerdicttype, supporting registration, reauthentication, and SSH checks #1850
Configuration
- Add
node.expiryconfiguration option to set a default node key expiry for nodes registered via auth key #3122- Tagged nodes (registered with tagged pre-auth keys) are exempt from default expiry
oidc.expiryhas been removed; usenode.expiryinstead (applies to all registration methods including OIDC)ephemeral_node_inactivity_timeoutis deprecated in favour ofnode.ephemeral.inactivity_timeout
- Add
trusted_proxiesto gateTrue-Client-IP/X-Real-IP/X-Forwarded-For(previously honoured from any client) #3268
Debug
- Add node connectivity ping page for verifying control-plane reachability #3183
- Omit secret fields (
Pass,ClientSecret,APIKey) from/debug/configJSON output #3180 - Route
statsvizthroughtsweb.Protected#3180
Other
- Remove old migrations for the debian package #3185
- Install
config-example.yamlas example for the debian package #3186 - Fix user-owned re-registration with zero client expiry and no default storing
0001-01-01 00:00:00in the database instead ofNULL#3199- Pre-existing rows with
0001-01-01 00:00:00are not backfilled; they clear themselves the next time the node re-registers
- Pre-existing rows with
- Fix
tailscaledrestart on a node with no expiry resettingNULLto0001-01-01 00:00:00in the database, affecting both tagged and untagged nodes #3197
Upgrade
Please follow the steps outlined in the upgrade guide to update your existing Headscale installation.
Changelog
- 7f02210 .golangci: ignore tests for goconst, raise occurrence threshold
- 157e3a3 AGENTS.md: trim to behavioural guidance, drop deprecated sub-agent
- cfb308b Add FAQ entry to migrate back to default IP prefixes
- e597f4c Add Headscale UI to web UI documentation
- df339cd Add a link to Authentik's integration guide
- f3f84a5 Add docs for policy-wide options and node attributes
- 890a044 Add more UIs
- 4eb5899 Add taildrive, tests, sshTests as supported features
- 542091e Add unit test
- 20dff82 CHANGELOG: add minimum Tailscale version for 0.29.0
- 58a85b6 CHANGELOG: bump 0.29.0 minimum tailscale client to v1.80.0
- f693cc0 CHANGELOG: document grants support for 0.29.0
- 4e1d83e CHANGELOG: document hostname cleanroom rewrite
- b52f8cb CHANGELOG: document node.expiry and oidc.expiry deprecation
- 408f402 CHANGELOG: document nodeAttrs feature and migrations
- f03d41e CHANGELOG: document policy tests (beta)
- e78a24b CHANGELOG: document sshTests evaluation (beta)
- 30d1857 CHANGELOG: document strict version upgrade path
- ec48f34 CHANGELOG: document subnet-to-subnet ACL fixes
- fd10741 CHANGELOG: document user-facing changes from #3180
- bcfaf6a CHANGELOG: note nil expiry preservation fix
- e3323b6 Describe how to set username instead of SPN for Kanidm
- dc73376 Dockerfile.tailscale-HEAD,Dockerfile.derper: bump golang to 1.26.3
- 78570c7 Dockerfile: bump base images
- e40dbe3 Dockerfile: bump tailscale DERPer builder to Go 1.26.2
- 6390fce Dockerfile: bump tailscale HEAD builder to Go 1.26.2
- 7e6c792 Document availability of autgroup:internet
- faf55f5 Document how to use the provider identifier in the policy
- 1a64d95 Document supported autogroups once
- 0f12e41 Explain one approach to update OIDC provider info
- efd83da Explicitly mention that a headscale username should not end with @
- d556df1 Extend upgrade guide with backup instructions
- 4460055 Fix invisible selected menu item
- a0d6802 Fix minor formatting issue in FAQ
- c7f221d Fix typo and wording
- 3672a2d Fix typo in API key creation help text
- 414d3bb Fix typo in comment about fsnotify behavior
- c907b0d Fix version in mkdocs
- 32e1d77 Install config-example.yaml as example for the debian package
- 9e50071 Link Fosdem 2026 talk
- 84c7f0d Link to development builds
- 8028fa5 No longer consider autogroup:self experimental
- e07b391 Quote autogroup:self in the CHANGELOG
- acddd73 Reformat docs with mdformat
- 109bfc4 Refresh docs for Grants
- c4ab267 Refresh features page
- 8f60b81 Refresh update path
- c29bcd2 Release planning happens in milestones
- 9ea09ea Remove changelog section for 0.28.1
- 14ce7e9 Remove link to Arch AUR headscale-git
- 4844628 Remove link to sqlite
- 892ffff Remove misleading comment
- 61c9ae8 Remove old migrations for the debian package
- e13f045 Remove redundant prefix
- 4bb0241 Require to update from one version to the next
- edb7ad0 Rewrite ACL docs as policy
- 45b698d Shorten container introduction
- 513544c Simplify upgrade snippet with a link to the upgrade guide
- 8423af2 Swap favicon for updated version
- 47307d1 Switch to mdformat to format docs
- f3512d5 Switch to mkdocs-materialx
- e285f3c The headscale service is enabled by default
- 3557333 Update config-example links
- 9baa795 Update docs for auth-id changes
- 813eb2d Update docs for new HA tracking
- f1494a3 Update links to Tailscale documentation
- fda72ad Update main.md
- 68b0014 Use distroless without quotes
- 163363a Use docs instead of KB
- 23a5f1b Use pymdownx.magiclink with its default configuration
- 97778c9 all: add tests for PingRequest implementation
- 17236fd all: annotate complex functions with gocyclo rationale
- 3e2aa58 all: annotate gosec false positives with rationale
- 93860a5 all: apply formatter changes
- 4cca631 all: apply godoc [Name] link conventions across comments
- 43afeed all: apply golangci-lint 2.9.0 fixes
- ce580f8 all: fix golangci-lint issues (#3064)
- eccf64e all: fix staticcheck SA4006 in types_test.go
- 3e0a96e all: fix test flakiness and improve test infrastructure
- b113655 all: implement PingRequest for node connectivity checking
- f905d58 all: mechanical lint fixes
- 742878d all: regenerate generated files for new tool versions
- 010a556 all: rephrase prose to fit codebase voice
- 36a73f8 all: update Go dependencies
- 542cdb2 all: update Go to 1.26.1
- 70f8141 all: upgrade from Go 1.26rc2 to Go 1.26.0
- 0f6d312 all: upgrade to Go 1.26rc2 and modernize codebase
- 4a9a329 all: use lowercase log messages
- 0567cb6 app: add security headers middleware
- f7d8bb8 app: remove gRPC reflection from remote server
- 3033844 app: switch from gorilla to chi mux
- 8a97dd1 app: wire HA health prober into scheduled tasks
- f066d12 assets: fix logo alignment and error icon centering
- 41d70fe auth: check machine key on tailscaled-restart fast path
- cb3b694 auth: generalise auth flow and introduce AuthVerdict
- 25ccb5a build: update golangci-lint and gopls in flake
- eb23c12 capver, types: bump to tailscale v1.98, drop LegacyDERPString
- 442fcdb capver: regenerate for tailscale v1.96
- 31c4331 capver: regenerate from docker tags
- 2530d86 change: document PingRequest merge first-wins foot-gun
- 4a4032a changelog: document filter rule merging
- f27298c changelog: document wildcard CGNAT range change Add breaking change entry for the wildcard resolution change to use CGNAT/ULA ranges instead of all IPs. Updates #3036
- 575d8ec changelog: normalise 0.29.0 BREAKING and Changes sections
- 9621a97 ci, pre-commit: validate vendor hash via vendorhash check
- e171d30 ci: add build workflow for main branch
- 99a93c1 ci: add rolling development tag to container builds
- 795a1ef ci: fetch full history in golangci-lint job
- d15ec28 ci: pin Docker to v28 to avoid v29 breaking changes
- 1b6ab52 ci: regenerate integration test workflow
- 0f97294 ci: regenerate integration test workflow
- 5c449db ci: regenerate test-integration.yaml for TestSSHLocalpart
- a7d405a ci: regenerate test-integration.yaml for TestTailscaleRustAxum
- 1f9635c ci: restrict test generator to .go files
- a76b4bd ci: switch integration tests to ARM runners
- 4d3b567 ci: use overlay2 storage driver instead of pinning docker v28
- e00c899 cmd, templates, integration: extract shared production constants
- 461a0e2 cmd/dev: add local development server tool
- 1a58b77 cmd/dev: validate --port fits the derived-port range
- 6c08b49 cmd/headscale/cli: add confirmAction helper for force/prompt patterns
- aae2f7d cmd/headscale/cli: add grpcRun wrapper for gRPC client lifecycle
- 7b7b270 cmd/headscale/cli: add mustMarkRequired helper for init-time flag validation
- d6c39e6 cmd/headscale/cli: add printListOutput to centralise table-vs-JSON branching
- 095106f cmd/headscale/cli: convert remaining commands to RunE
- 22fccae cmd/headscale/cli: deduplicate expiration parsing and api-key flag validation
- 2765fd3 cmd/headscale/cli: drop dead flag-read error checks
- af777f4 cmd/headscale/cli: extract bypassDatabase helper and simplify policy file reads
- 92a9acc cmd/headscale/cli: mention sshTests in policy check help
- 7460bec cmd/headscale/cli: move errMissingParameter and Error type to their users
- 8891ec9 cmd/headscale/cli: remove deprecated output, SuccessOutput, ErrorOutput
- d72a06c cmd/headscale/cli: remove legacy namespace and machine aliases
- 13ebea1 cmd/headscale/cli: remove nil resp guards and unexport HasMachineOutputFlag
- e816397 cmd/headscale/cli: remove no-op Args functions from serveCmd and dumpConfigCmd
- e6546b2 cmd/headscale/cli: silence cobra error/usage output and centralise error formatting
- e4fe216 cmd/headscale/cli: switch to RunE with grpcRunE and error returns
- ca321d3 cmd/headscale/cli: use HeadscaleDateTimeFormat and util.Base10 consistently
Note truncated.
-
v0.28.1-0.20260402192427-23a5f1b6285502 Apr 2026 pre-releaseNothing published for this version
-
v0.28.1-0.20260401131042-4f040dead2ba01 Apr 2026 pre-releaseNothing published for this version
-
v0.28.1-0.20260319060835-568baf3d021b19 Mar 2026 pre-releaseNothing published for this version
-
v0.28.1-0.20260306131844-65880ecb586706 Mar 2026 pre-releaseNothing published for this version
-
v0.28.1-0.20260228131411-5c449db1258928 Feb 2026 pre-releaseNothing published for this version
-
v0.28.1-0.20260207072351-20dff82f95e307 Feb 2026 pre-releaseNothing published for this version
-
v0.28.1-0.20260206064029-bfb6fd80dfa906 Feb 2026 pre-releaseNothing published for this version
-
v0.28.004 Feb 2026Release notes
Open source →Minimum supported Tailscale client version: v1.74.0
Tags as identity
Tags are now implemented following the Tailscale model where tags and user ownership are mutually exclusive. Devices can be either user-owned (authenticated via web/OIDC) or tagged (authenticated via tagged PreAuthKeys). Tagged devices receive their identity from tags rather than users, making them suitable for servers and infrastructure. Applying a tag to a device removes user-based ownership. See the Tailscale tags documentation for details on how tags work.
User-owned nodes can now request tags during registration using
--advertise-tags. Tags are validated against thetagOwnerspolicy and applied at registration time. Tags can be managed via the CLI or API after registration. Tagged nodes can return to user-owned by re-authenticating withtailscale up --advertise-tags= --force-reauth.A one-time migration will validate and migrate any
RequestTags(stored in hostinfo) to the tags column. Tags are validated against your policy'stagOwnersrules during migration. #3011Smarter map updates
The map update system has been rewritten to send smaller, partial updates instead of full network maps whenever possible. This reduces bandwidth usage and improves performance, especially for large networks. The system now properly tracks peer changes and can send removal notifications when nodes are removed due to policy changes. #2856 #2961
Pre-authentication key security improvements
Pre-authentication keys now use bcrypt hashing for improved security #2853. Keys are stored as a prefix and bcrypt hash instead of plaintext. The full key is only displayed once at creation time. When listing keys, only the prefix is shown (e.g.,
hskey-auth-{prefix}-***). All new keys use the formathskey-auth-{prefix}-{secret}. Legacy plaintext keys in the format{secret}will continue to work for backwards compatibility.Web registration templates redesign
The OIDC callback and device registration web pages have been updated to use the Material for MkDocs design system from the official documentation. The templates now use consistent typography, spacing, and colours across all registration flows.
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
- API clients should use the
-
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 --id <ID>replaces--user <USER> <KEY>headscale preauthkeys delete --id <ID>replaces--user <USER> <KEY>
Before:
headscale preauthkeys create --user 1 --reusable --tags tag:server headscale preauthkeys list --user 1 headscale preauthkeys expire --user 1 <KEY> headscale preauthkeys delete --user 1 <KEY>After:
headscale preauthkeys create --reusable --tags tag:server headscale preauthkeys list headscale preauthkeys expire --id 123 headscale preauthkeys delete --id 123 -
Tags: The gRPC
SetTagsendpoint 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
--advertise-tagsis processed during registration, not on every policy evaluation- PreAuthKey tagged devices ignore
--advertise-tagsfrom clients - User-owned nodes can use
--advertise-tagsif authorized bytagOwnerspolicy - Tags can be managed via CLI (
headscale nodes tag) or the SetTags API after registration
-
Database migration support removed for pre-0.25.0 databases #2883
- If you are running a version older than 0.25.0, you must upgrade to 0.25.1 first, then upgrade to this release
- See the upgrade path documentation for detailed guidance
- In version 0.29, all migrations before 0.28.0 will also be removed
-
Remove ability to move nodes between users #2922
- The
headscale nodes moveCLI command has been removed - The
MoveNodeAPI endpoint has been removed - Nodes are permanently associated with their user or tag at registration time
- The
-
Add
oidc.email_verified_requiredconfig option to control email verification requirement #2860- When
true(default), only verified emails can authenticate via OIDC in conjunction withoidc.allowed_domainsoroidc.allowed_users. Previous versions allowed to authenticate with an unverified email but did not store the email address in the user profile. This is now rejected during authentication with anunverified emailerror. - When
false, unverified emails are allowed for OIDC authentication and the email address is stored in the user profile regardless of its verification state.
- When
-
SSH Policy: Wildcard (
*) is no longer supported as an SSH destination #3009- Use
autogroup:memberfor user-owned devices - Use
autogroup:taggedfor tagged devices - Use specific tags (e.g.,
tag:server) for targeted access
Before:
{ "action": "accept", "src": ["group:admins"], "dst": ["*"], "users": ["root"] }After:
{ "action": "accept", "src": ["group:admins"], "dst": ["autogroup:member", "autogroup:tagged"], "users": ["root"] } - Use
-
SSH Policy: SSH source/destination validation now enforces Tailscale's security model #3010
Per Tailscale SSH documentation, the following rules are now enforced:
- Tags cannot SSH to user-owned devices: SSH rules with
tag:*orautogroup:taggedas source cannot have username destinations (e.g.,alice@) orautogroup:member/autogroup:selfas destination - Username destinations require same-user source: If destination is a specific username (e.g.,
alice@), the source must be that exact same user only. Useautogroup:selffor same-user SSH access instead
Invalid policies now rejected at load time:
// INVALID: tag source to user destination {"src": ["tag:server"], "dst": ["alice@"], ...} // INVALID: autogroup:tagged to autogroup:member {"src": ["autogroup:tagged"], "dst": ["autogroup:member"], ...} // INVALID: group to specific user (use autogroup:self instead) {"src": ["group:admins"], "dst": ["alice@"], ...}Valid patterns:
// Users/groups can SSH to their own devices via autogroup:self {"src": ["group:admins"], "dst": ["autogroup:self"], ...} // Users/groups can SSH to tagged devices {"src": ["group:admins"], "dst": ["autogroup:tagged"], ...} // Tagged devices can SSH to other tagged devices {"src": ["autogroup:tagged"], "dst": ["autogroup:tagged"], ...} // Same user can SSH to their own devices {"src": ["alice@"], "dst": ["alice@"], ...} - Tags cannot SSH to user-owned devices: SSH rules with
Changes
- Smarter change notifications send partial map updates and node removals instead of full maps #2961
- Send lightweight endpoint and DERP region updates instead of full maps #2856
- Add NixOS module in repository for faster iteration #2857
- Add favicon to webpages #2858
- Redesign OIDC callback and registration web templates #2832
- Reclaim IPs from the IP allocator when nodes are deleted #2831
- Add bcrypt hashing for pre-authentication keys #2853
- Add prefix to API keys (
hskey-api-{prefix}-{secret}) #2853 - Add prefix to registration keys for web authentication tracking (
hskey-reg-{random}) #2853 - Tags can now be tagOwner of other tags #2930
- Add
taildrop.enabledconfiguration option to enable/disable Taildrop file sharing #2955 - Allow disabling the metrics server by setting empty
metrics_listen_addr#2914 - Log ACME/autocert errors for easier debugging #2933
- Improve CLI list output formatting #2951
- Use Debian 13 distroless base images for containers #2944
- Fix ACL policy not applied to new OIDC nodes until client restart #2890
- Fix autogroup:self preventing visibility of nodes matched by other ACL rules #2882
- Fix nodes being rejected after pre-authentication key expiration #2917
- Fix list-routes command respecting identifier filter with JSON output #2927
- Add
--idflag to expire/delete commands as alternative to--prefixfor API Keys #3016
-
-
v0.28.0-beta.222 Jan 2026 pre-releaseNothing published for this version
-
v0.28.0-beta.1.0.20251223192257-9146140217ec23 Dec 2025 pre-releaseNothing published for this version
-
v0.28.0-beta.118 Dec 2025 pre-releaseNothing published for this version
-
v0.27.2-rc.130 Nov 2025 pre-releaseNothing published for this version
-
v0.27.2-0.20251115164400-e0c9e18e222a15 Nov 2025 pre-releaseNothing published for this version
-
v0.27.2-0.20251114135127-21af106f68fa14 Nov 2025 pre-releaseNothing published for this version
-
v0.27.2-0.20251113193849-7fb0f9a5012013 Nov 2025 pre-releaseNothing published for this version
-
v0.27.2-0.20251113104410-75247f82b85a13 Nov 2025 pre-releaseNothing published for this version
-
v0.27.2-0.20251113061522-665cc440949813 Nov 2025 pre-releaseNothing published for this version
-
v0.27.2-0.20251112192654-8394e7094a4212 Nov 2025 pre-releaseNothing published for this version
-
v0.27.2-0.20251112153636-da9018a0ebc212 Nov 2025 pre-releaseNothing published for this version
-
v0.27.2-0.20251112034657-218a8db1b90112 Nov 2025 pre-releaseNothing published for this version
-
v0.27.2-0.20251111232700-1dcb04ce9b2f11 Nov 2025 pre-releaseNothing published for this version
-
v0.27.2-0.20251111193523-6d24afba1ce711 Nov 2025 pre-releaseNothing published for this version
-
v0.27.111 Nov 2025Release notes
Open source →Minimum supported Tailscale client version: v1.64.0
Changes
- Expire nodes with a custom timestamp #2828
- Fix issue where node expiry was reset when tailscaled restarts #2875
- Fix OIDC authentication when multiple login URLs are opened #2861
- Fix node re-registration failing with expired auth keys #2859
- Remove old unused database tables and indices #2844 #2872
- Ignore litestream tables during database validation #2843
- Fix exit node visibility to respect ACL rules #2855
- Fix SSH policy becoming empty when unknown user is referenced #2874
- Fix policy validation when using bypass-grpc mode #2854
- Fix autogroup:self interaction with other ACL rules #2842
- Fix flaky DERP map shuffle test #2848
- Use current stable base images for Debian and Alpine containers #2827
-
v0.27.1-0.20251111111259-4728a2ba9ea611 Nov 2025 pre-releaseNothing published for this version
-
v0.27.1-0.20251110193412-21e3f2598de610 Nov 2025 pre-releaseNothing published for this version
-
v0.27.1-0.20251102121959-2024219bd10a02 Nov 2025 pre-releaseNothing published for this version
-
v0.27.1-0.20251101070913-f9bb88ad24d901 Nov 2025 pre-releaseNothing published for this version
-
v0.27.1-0.20251028045526-0a43aab8f5c828 Oct 2025 pre-releaseNothing published for this version
-
v0.27.1-0.20251027111402-19a33394f6e027 Oct 2025 pre-releaseNothing published for this version
-
v0.27.027 Oct 2025Release notes
Open source →Minimum supported Tailscale client version: v1.64.0
Database integrity improvements
This release includes a significant database migration that addresses longstanding issues with the database schema and data integrity that has accumulated over the years. The migration introduces a
schema.sqlfile as the source of truth for the expected database schema to ensure new migrations that will cause divergence does not occur again.These issues arose from a combination of factors discovered over time: SQLite foreign keys not being enforced for many early versions, all migrations being run in one large function until version 0.23.0, and inconsistent use of GORM's AutoMigrate feature. Moving forward, all new migrations will be explicit SQL operations rather than relying on GORM AutoMigrate, and foreign keys will be enforced throughout the migration process.
We are only improving SQLite databases with this change - PostgreSQL databases are not affected.
Please read the PR description for more technical details about the issues and solutions.
SQLite Database Backup Example:
# Stop headscale systemctl stop headscale # Backup sqlite database cp /var/lib/headscale/db.sqlite /var/lib/headscale/db.sqlite.backup # Backup sqlite WAL/SHM files (if they exist) cp /var/lib/headscale/db.sqlite-wal /var/lib/headscale/db.sqlite-wal.backup cp /var/lib/headscale/db.sqlite-shm /var/lib/headscale/db.sqlite-shm.backup # Start headscale (migration will run automatically) systemctl start headscaleDERPMap update frequency
The default DERPMap update frequency has been changed from 24 hours to 3 hours. If you set the
derp.update_frequencyconfiguration option, it is recommended to change it to3hto ensure that the headscale instance gets the latest DERPMap updates when upstream is changed.Autogroups
This release adds support for the three missing autogroups:
self(experimental),member, andtagged. Please refer to the documentation for a detailed explanation.autogroup:selfis marked as experimental and should be used with caution, but we need help testing it. Experimental here means two things; first, generating the packet filter from policies that useautogroup:selfis very expensive, and it might perform, or straight up not work on Headscale installations with a large number of nodes. Second, the implementation might have bugs or edge cases we are not aware of, meaning that nodes or users might gain more access than expected. Please report bugs.Node store (in memory database)
Under the hood, we have added a new datastructure to store nodes in memory. This datastructure is called
NodeStoreand aims to reduce the reading and writing of nodes to the database layer. We have not benchmarked it, but expect it to improve performance for read heavy workloads. We think of it as, "worst case" we have moved the bottle neck somewhere else, and "best case" we should see a good improvement in compute resource usage at the expense of memory usage. We are quite excited for this change and think it will make it easier for us to improve the code base over time and make it more correct and efficient.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-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
Changes
- Database schema migration improvements for SQLite #2617
- IMPORTANT: Backup your SQLite database before upgrading
- Introduces safer table renaming migration strategy
- Addresses longstanding database integrity issues
- Add flag to directly manipulate the policy in the database #2765
- DERPmap update frequency default changed from 24h to 3h #2741
- DERPmap update mechanism has been improved with retry, and is now failing conservatively, preserving the old map upon failure. #2741
- Add support for
autogroup:member,autogroup:tagged#2572 - Fix bug where return routes were being removed by policy #2767
- Remove policy v1 code #2600
- Refactor Debian/Ubuntu packaging and drop support for Ubuntu 20.04. #2614
- Remove redundant check regarding
noiseconfig #2658 - Refactor OpenID Connect documentation #2625
- Don't crash if config file is missing #2656
- Adds
/robots.txtendpoint to avoid crawlers #2643 - OIDC: Use group claim from UserInfo #2663
- OIDC: Update user with claims from UserInfo before comparing with allowed groups, email and domain #2663
- Policy will now reject invalid fields, making it easier to spot spelling errors #2764
- Add FAQ entry on how to recover from an invalid policy in the database #2776
- EXPERIMENTAL: Add support for
autogroup:self#2789 - Add healthcheck command #2659
-
v0.27.0-beta.2.0.20251024152253-e68e2288f79924 Oct 2025 pre-releaseNothing published for this version
-
v0.27.0-beta.223 Oct 2025 pre-releaseNothing published for this version
-
v0.27.0-beta.1.0.20251018055659-46477b80214818 Oct 2025 pre-releaseNothing published for this version
-
v0.27.0-beta.117 Oct 2025 pre-releaseNothing published for this version
-
v0.26.106 Jun 2025Release notes
Open source →Changes
- Ensure nodes are matching both node key and machine key when connecting. #2642
-
v0.26.1-0.20251011035739-c4a8c038cdd611 Oct 2025 pre-releaseNothing published for this version
-
v0.26.1-0.20250917143229-bd35fcf338d617 Sep 2025 pre-releaseNothing published for this version
-
v0.26.1-0.20250913061501-30d12dafed2113 Sep 2025 pre-releaseNothing published for this version
-
v0.26.1-0.20250827150913-8e25f7f9dd1227 Aug 2025 pre-releaseNothing published for this version
-
v0.26.1-0.20250815151138-30a1f7e68e1715 Aug 2025 pre-releaseNothing published for this version
-
v0.26.1-0.20250704154701-855c48aec28704 Jul 2025 pre-releaseNothing published for this version
-
v0.26.1-0.20250704125817-ded049b9055504 Jul 2025 pre-releaseNothing published for this version
-
v0.26.1-0.20250704120059-3bad5d55909804 Jul 2025 pre-releaseNothing published for this version
-
v0.26.1-0.20250704085137-d461db3abdf204 Jul 2025 pre-releaseNothing published for this version
-
v0.26.1-0.20250624124544-e73b2a9fb9db24 Jun 2025 pre-releaseNothing published for this version
-
v0.26.1-0.20250606101411-bad783321e8e06 Jun 2025 pre-releaseNothing published for this version
{ "randomizeClientPort": true, "nodeAttrs": [ { "target": ["autogroup:tagged"], "attr": ["disable-captive-portal-detection"] }, { "target": ["[email protected]"], "attr": ["nextdns:abc123"] }, ], }