UI walkthrough
A page-by-page tour of the headscale-admin console: what each view shows, what you can do there, and where the data comes from.
The console is a Svelte single-page app served by the same binary that supervises headscale, under /admin. The
sidebar groups pages into four areas:
- Overview - the dashboard.
- Network - Nodes, Routes & exit nodes, DNS.
- Access - Users, Pre-auth keys, API keys, ACL policy, Access map, and (when enabled) User sync.
- System - Audit log, Admins, Settings, Backup. Admin-only.
Operators see Overview, Network, and Access; admins additionally see the System group. The screenshots below were captured against a synthetic dev dataset (15 users, 15 nodes, a representative ACL policy) - your data will look the same.
Connect a device

The public signup page (the default landing for /) is where someone onboards a machine. Pick a platform - Apple,
Linux, or Windows - and it expands with copy-paste install and tailscale up --login-server … steps pre-filled with
this server’s URL. It needs no login, so you can hand the link to a teammate to join the tailnet. (When the admin UI is
IP-restricted you can still keep just this page public with server.public_signup.)
Sign in
The login screen appears when authentication is enabled. Local sign-in is email + password; if a second factor is
set up, it then asks for a TOTP code (or a recovery code). With OIDC configured, a Sign in with SSO button
appears, and with passkeys registered, a Sign in with a passkey button. When auth.disabled: true, there is no
login screen at all - every visitor is treated as admin (local development only).
Overview

The dashboard summarises the tailnet at a glance: clickable tiles for nodes (online / total), users,
pre-auth keys, and API keys; a control-server status card (headscale-admin and headscale health +
versions, and the client-facing control URL); a ready-to-copy tailscale up / tailscale login snippet; and a
recently added nodes grid with each machine’s addresses. A red banner appears here if headscale can’t be reached.
Nodes

Every machine registered with the control server. Search by name / user / IP / tag, filter by last-seen, and sort any column. Rows show IPs, ACL tags, route counts (advertised / approved), online status, and last-seen. The toolbar has register node, backfill IPs, and refresh.
Clicking a node opens its detail modal:

The modal shows the full record - status, IDs, hostname, user, addresses, tags, advertised and approved routes (here an
exit node advertising 0.0.0.0/0, ::/0), timestamps and key expiry - and the per-node actions: Rename, Edit
tags, Edit routes, Expire, Delete.
Routes & exit nodes

Subnet routes and exit nodes in one place. Tiles up top count advertised, approved, pending, and exit
nodes. The Subnet routes table lists each advertised CIDR, the node and owner advertising it, its approval state,
and an enable toggle; Exit nodes does the same for machines offering 0.0.0.0/0, ::/0. Approving or revoking a
route is a single toggle.
DNS

MagicDNS and resolver configuration, written straight into headscale.yaml. Toggle MagicDNS, set the base
domain, manage global nameservers, add split-DNS domains routed to private resolvers, force clients onto your
nameservers with override local DNS, and add search domains. Because these land in headscale’s config, the page
notes when a headscale restart is needed for changes to take effect.
Users

Tailnet identities. Search by name / email / provider, and create users with + new user. Each row shows the
provider (e.g. oidc or local) and creation time; clicking a user opens a detail modal with their ACL groups and
ownable tags (read from the policy) and the nodes they own. The tagged-devices chip surfaces machines owned by an
ACL tag: rather than a real user.
Pre-auth keys

Pre-authentication keys for non-interactive onboarding (tailscale up --auth-key=…). The list shows each key’s user,
reusable / ephemeral / used flags, ACL tags, and expiry. The + new key modal (above) picks the user, the
reusable and ephemeral flags, an expiration (e.g. 24h, 7d, or 0 for none), and optional ACL tags.
The minted key is shown once with a ready-to-run tailscale up command - copy it before closing.
API keys

Bearer keys for headscale’s own API, used by the headscale CLI and other tooling. Each row shows the key prefix and
its created / expires timestamps, with expire and delete actions. As with pre-auth keys, a newly created key is
revealed only once.
ACL policy

Who can reach what across the tailnet. The visual editor has a tab per policy section - Groups, Tag owners,
Hosts, Auto-approvers, ACL rules, Grants, Node attrs, SSH - each row reorderable and editable,
with “+ from known” pickers that pull existing users, groups, and tags. The JSON editor toggle exposes the raw
HuJSON with format / validate. The header shows the policy mode (database or file) and last-updated time;
Validate checks the policy and Save policy applies it. In file mode, saving notes that a headscale restart is
required.
Access map

A live graph of the effective access policy - ACL rules and grants rendered as edges between users, groups, tags,
hosts, and nodes, annotated with the allowed ports (e.g. group:ops → tag:prod on 22, 443). Search to focus an
entity’s neighbourhood, fold users into group badges or devices into tag counts, and export PNG. It’s the fastest
way to sanity-check “can X actually reach Y?” after a policy edit.
User sync

Shown only when headscale_pf.enabled: true. Fills the group: members of your ACL policy from an external identity
source using the bundled headscale-pf. Click Sync to pull the latest
membership and preview the diff against the current policy; review it, then Save to apply over the local
socket. Source credentials live only in headscale-admin’s config and are never exposed to the browser.
Audit log

An immutable record of every state-changing action through the console - user / node / key edits, policy and config changes, and login attempts - newest first. Tiles count events, unique actors, failures, and action types in the window. Free-text search spans target, IP, actor, action, and the detail payload; an exact-action filter and a time-range selector narrow further. Each row expands to its full event payload. Admins can prune old entries; a daily auto-prune runs per the configured retention.
Admins

Who can sign in to the console and what they can do. Each principal has a role (admin or operator), a source
(local or oidc), an enabled flag, and a last-login time. Create principals with + new principal (email, name,
role, and - in local mode - a password). When OIDC is enabled, password login is disabled and roles follow the IdP
group mapping; pre-creating a principal here pre-assigns a role by email, and pinned admins stay admin regardless of
group changes.
Settings

The supervised headscale’s configuration, plus deployment info. The top section reports the authentication mode,
the client-facing control server URL, and the running headscale-admin / headscale versions - with a
Download & switch control to move the supervised headscale to another release (checksum-verified, restarts
headscale, and auto-reverts on failure). Below, the User sync (headscale-pf) card shows the configured source and
tool version, and the rest of the page is a structured editor over headscale.yaml, with the raw file available in a
collapsible at the bottom. Saving rewrites headscale.yaml (a .bak is written first) and flags when a restart is
needed.
Backup

One-click exports of the pieces that make up this deployment: the ACL policy (HuJSON), the headscale-admin
database, the headscale database, or everything in a single archive. The database and full-archive exports
briefly stop headscale for a consistent snapshot. Files contain secrets (keys, sessions) - store them accordingly. To
restore, stop headscale-admin, put the files back under work_dir, and start it again.