Headscale v0.28.0-beta.2

v0.28.0-beta.2Pre-release

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 the tagOwners policy 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 with tailscale 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's tagOwners rules during migration. #3011

Smarter 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 format hskey-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, and ValidTags fields have been removed and replaced with a single Tags field that contains the node's applied tags #2993

    • API clients should use the Tags field instead of ValidTags
    • The headscale nodes list CLI command now always shows a Tags column and the --tags flag has been removed
  • PreAuthKey CLI: Commands now use ID-based operations instead of user+key combinations #2992

    • headscale preauthkeys create no longer requires --user flag (optional for tracking creation)
    • headscale preauthkeys list lists all keys (no longer filtered by user)
    • headscale preauthkeys expire --id <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 SetTags endpoint now allows converting user-owned nodes to tagged nodes by setting tags. #2885

  • Tags: Tags are now resolved from the node's stored Tags field only #2931

    • --advertise-tags is processed during registration, not on every policy evaluation
    • PreAuthKey tagged devices ignore --advertise-tags from clients
    • User-owned nodes can use --advertise-tags if authorized by tagOwners policy
    • 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 move CLI command has been removed
    • The MoveNode API endpoint has been removed
    • Nodes are permanently associated with their user or tag at registration time
  • Add oidc.email_verified_required config option to control email verification requirement #2860

    • When true (default), only verified emails can authenticate via OIDC in conjunction with oidc.allowed_domains or oidc.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 an unverified email error.
    • When false, unverified emails are allowed for OIDC authentication and the email address is stored in the user profile regardless of its verification state.
  • SSH Policy: Wildcard (*) is no longer supported as an SSH destination #3009

    • Use autogroup:member for user-owned devices
    • Use autogroup:tagged for 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"] }
    
  • SSH Policy: SSH source/destination validation now enforces Tailscale's security model #3010

    Per Tailscale SSH documentation, the following rules are now enforced:

    1. Tags cannot SSH to user-owned devices: SSH rules with tag:* or autogroup:tagged as source cannot have username destinations (e.g., alice@) or autogroup:member/autogroup:self as destination
    2. Username destinations require same-user source: If destination is a specific username (e.g., alice@), the source must be that exact same user only. Use autogroup:self for 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@"], ...}
    
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.enabled configuration 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
  • API Key CLI: Add --id flag to expire/delete commands as alternative to --prefix #3016
    • headscale apikeys expire --id <ID> or --prefix <PREFIX>
    • headscale apikeys delete --id <ID> or --prefix <PREFIX>
Upgrade

Please follow the steps outlined in the upgrade guide to update your existing Headscale installation.

It's best to update from one stable version to the next (e.g., 0.24.0 → 0.25.1 → 0.26.1) in case you are multiple releases behind. You should always pick the latest available patch release.

Be sure to check the changelog above for version-specific upgrade instructions and breaking changes.

Backup Your Database

Always backup your database before upgrading. Here's how to backup a SQLite database:

# 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 headscale
Changelog
  • 9146140217ecdd500d64a3648d04a2f2489910de Add headscale-operator
  • bb30208f97d8b02e7f8ea13f6972f00b6dd209df Add headscale-piying web UI to docs
  • 25a74348304de99df372926ae5920035dfc09fe5 Bump version in mkdocs
  • e43f19df794158328729c47f479069ec4fdafaf8 CHANGELOG: add breaking change for Node API simplification
  • d50108c722fb8a13c1920d9a5c2dfbec15b36bce Changelog: mark oidc.email_verified_required as breaking
  • c3e2e57f8e6e68deb66eed8e0f76c5d229791d85 Clarify autogroup:member
  • 6d21a4a3fed44fd5e0c09d4ff8c219d42ef2533b Document /version in the API docs
  • 7d81dca9aaae7bbf5ed3047039805aab8f7e37f7 Document how to disable the metrics interfaces
  • 99d35fbbbc3a9d58d33029bfd993932f982596b7 Document oidc.email_verification_required
  • 183a38715c045e8e0b5ec7f71d395c2f65970210 Fix list-routes examples
  • 8387c9cd82ecd0d441a35051d02389c838fed801 Fix ownership description for auto approved routers/exits
  • 18e13f6ffa61d89eb084154f2a42bc300e07e2f5 Link to headscale.net for docs
  • a445278f7601f1d95600fca390275e1476ef182d Mention tags on the features page
  • eec54cbbf347c0c612462d91b7e67b3d299e7546 api/v1: replace ForcedTags/InvalidTags/ValidTags with Tags
  • 606e5f68a0af9d87adfcaeda39450c998d716e0c changelog: fixups for 0.28.0-beta.2
  • c1cfb59b91645dd18a5e7fc3088c3c8f54146e10 ci: add ACL unknown user tests to integration workflow
  • 1325fd8b271c4cf0fa274e10541775e4519cb236 cli,hscontrol: use ID-based preauthkey operations
  • e0bae9b7697f984ecd3a5b6035cafc972d985d5a cli: add --id flag to API key expire/delete commands
  • 72fcb93ef3ae99a5f9fe769a51ea0d50ef702b97 cli: ensure tagged-devices is included in profile list (#2991)
  • 165c5f04915d9ce91bbf7dbf67558cfc203c2c6b cli: fix preauthkeys expire/delete argument validation
  • 951fd5a8e778253d33240651d83a48ccc5f1c059 cli: show Owner column in preauthkeys list
  • 6654142fbe9bfde2d40c4ed5e3e0d405d46dd618 cmd/headscale: migrate tests from check.v1 to testify
  • 0bcfdc29ada99b491cff641b386b28bff8bb4709 cmd/hi: enable concurrent test execution
  • 424e26d6365dcbd19ef1474e8d78fb09e5d7dc56 db: migrate tests from check.v1 to testify
  • 4e1834adaf0021c8ed22c3404ccbb081f9390ac7 db: use PolicyManager for RequestTags migration
  • aa29fd95a381af330543796f4253df9466abee38 derp: migrate to derpserver package API
  • 84c092a9f9875ed274aa40c9c14ebbcb05166f43 flake.lock: Update
  • 8631581852266a3720cd80fd37904e5e6c6fba3e gen: regenerate proto code
  • a04b21abc630f1a0fef34c158e3fcd7d6a1ae6fb gen: regenerate protobuf and type views
  • 8776745428f6330efd2c667b4e83ff863e443f31 gen: regenerate protobuf code
  • 0516c0ec375cb24fa657060a427f5d87945e405f gen: regenerate protobuf code
  • 515a22e696e403fa24021830ac21f76ab48230c3 go.mod: remove gopkg.in/check.v1 dependency
  • 0565e01c2fc8f8ac18952fb64d76229d0e5ede9a go.mod: update dependencies
  • a194712c34bdc8577e3aa18be7a4bd3ac8ef54b4 grpc: support expire/delete API keys by ID
  • c8c3c9d4a01181d2d82f2338256e0acad14445fb hscontrol: allow CreatePreAuthKey without user when tags provided
  • 3b4b9a443684858571edd2be70f407b3ad5de055 hscontrol: fix tag updates not propagating to node self view
  • 4ab06930a23c599e785dc61f08f223722e6ced34 hscontrol: handle tags-only PreAuthKeys in registration
  • 07a4b1b1fda371751eac2f1780df5dc0bec49a49 integration/tags: add dedicated issue #2978 reproduction test
  • 1b6db34b934b9c4d1c1fd2c4a9225c49db6eeb84 integration/tags: add self-tag validation to existing tests
  • 87c230d2513271964bd7e3e270924f50919b575d integration: add run ID isolation for concurrent test execution
  • 2e180d2587c56d0bc3e03daf1aa6807ff2587431 integration: add test for reauth tag removal
  • 98c0817b957d17b7787755a15cbc846c8f143023 integration: add tests for ACL group with deleted/unknown users
  • b3c4d0ec81d360bf1ed97ff646824aca402eb3c1 integration: add tests for API key expire/delete by ID
  • b8f3e09046f131e6e84c0e706c9b7e7d66095985 integration: fix tags-only auth key tests
  • 740d2b5a2c5cdf8c31378ab2bd0d129ffafc943d integration: support auth keys without user
  • 00da5361b3e3af966925048f9194b90eadf76957 integration: test tags-only auth key behavior
  • 4dd1b49a35d9100899771d6469a94d7215fe4c8b integration: update CLI tests for ID-based preauthkey commands
  • db6882b5f5ea5a1a2b11621bac214ef2c5a1fe8b integration: update DeleteAuthKey to use ID
  • e9a94f00a982f450a8c447d9eaa96f99a659611d integration: update SSH tests for validation rules
  • aee1d2a640584c7f50a0055d6dda13c53fc3b74b nix: fix deprecated attributes and update dev tools
  • 92caadcee642af42fbdaf5861d5dcb89056031dc nix: update vendor hash for Go dependencies
  • f5c779626ab1d32541c01fb0a93ac1c6e2373d86 nix: use testers.nixosTest instead of nixosTest
  • 5688c201e95e52bf299b158c65403eb9ed318023 policy/v2: validate SSH source/destination combinations
  • 22afb2c61b3fb6c24e409ac4d9d1f541e9de2885 policy: fix asymmetric peer visibility with autogroup:self
  • d40203e1534c4bc61212bcbf3df1c6b1464e6478 policy: update tests for SSH validation rules
  • b01eda721cd7fd2829f58fb124219a6488450bfa proto: add id field to API key expire/delete requests
  • 1398d01bd8ba445fc2a74c1a9fdb5eff04977087 proto: change preauthkey API to ID-based operations
  • 5103b35f3cfe988f96264399e26d2e6d88a1f7a3 sqliteconfig: add config opt for tx locking
  • 42bd9cd05817032f3e523dbbc120d0e601528c13 state: add GetAPIKeyByID method
  • d9cbb966032118546f795b67bda90adb5ed97cfe state: add unit test for DeleteUser change signal
  • 0451dd47181bb802b54f26b2e97dca05a4bd3ab7 state: allow untagging nodes via reauth with empty RequestTags
  • 00f22a84437a9ee61dcfa5139484ac3690a5e8f5 state: disable key expiry for nodes with approved advertise-tags
  • 1d9900273e206cc4546d8587b84b38d88284b6a1 state: disable key expiry for tagged nodes
  • 4be13baf3f680167ab09ea8634e19057e1095fbe state: update policy manager when deleting users
  • 3689f05407225f8ad29c913e2c5dcfd24b53a85f types: use Username() in User.Proto() when Name is empty
  • a6696582a439aedc8c5ff6b15e11c3f0c088fcb2 util/dns: fix variable redeclaration in ValidateDNSName
View original

Upgraded? How did it go?

Discussion