Headmaster is a modular, .env-driven admin UI for Headscale. It is built to stay deployment-agnostic, so operators can bring their own identity provider, hostnames, and runtime layout without patching application code.
- OIDC login with admin-group gating
- Server-side Headscale command adapter
- Nodes-first admin surface for users, nodes, preauth keys, API keys, routes, enrollment, policy, settings, and audit
- Docker Compose first, with configuration supplied through
.env
- Current version:
0.1.1 - Release model: SemVer
- Runtime model: Docker Compose first
- Auth model: OIDC with admin-group enforcement
- Headscale integration:
docker execby default, direct binary mode optional
- Default host port:
41869 - Default container port:
41869
- Docker and Docker Compose for the default deployment path
- A reachable Headscale instance
- An OIDC provider for authenticated administration
curl -fsSL https://raw.githubusercontent.com/Pragith/headmaster/main/install.sh | bashOr install from a local checkout:
cp .env.example .env
./bin/headmaster env:init
./bin/headmaster doctor
./bin/headmaster up- Copy
.env.exampleto.env - Set your public base URL, OIDC values, and Headscale execution mode
- Set either:
HEADMASTER_HEADSCALE_EXEC_MODE=dockerwithHEADMASTER_HEADSCALE_CONTAINER- or
HEADMASTER_HEADSCALE_EXEC_MODE=directwithHEADMASTER_HEADSCALE_BINARY
- Choose whether policy writes should target Headscale database mode or the mounted policy file path
- Start the stack with
./bin/headmaster up
Headmaster is configured entirely through environment variables. The sample file intentionally uses neutral placeholder values so it can be copied into any environment without leaking internal hostnames or usernames.
At minimum, configure:
HEADMASTER_PUBLIC_BASE_URLHEADMASTER_SESSION_SECRETHEADMASTER_AUTH_ISSUERHEADMASTER_AUTH_CLIENT_IDHEADMASTER_AUTH_CLIENT_SECRETHEADMASTER_AUTH_REDIRECT_URI- one Headscale execution path:
HEADMASTER_HEADSCALE_EXEC_MODE=dockerwithHEADMASTER_HEADSCALE_CONTAINER- or
HEADMASTER_HEADSCALE_EXEC_MODE=directwithHEADMASTER_HEADSCALE_BINARY
Optional settings include:
HEADMASTER_HEADSCALE_CONFIG_FILEwhen you want file-based policy fallback supportHEADMASTER_READ_ONLY=truefor a safer read-only operator modeHEADMASTER_AUDIT_LOG_PATHto relocate audit storage
Headmaster is not tied to Keycloak. Any OIDC provider can be used if it exposes a standard issuer URL, client ID, client secret, redirect URI, and group claim.
Keycloak-compatible providers can use scripts/bootstrap-keycloak-client.py to create the client from .env. Non-Keycloak providers should create the OIDC client in their own admin console and place the resulting values in .env.
For private networks where another layer already enforces access, set HEADMASTER_AUTH_ENABLED=false and keep Headmaster behind that trusted boundary.
cp .env.example .env
pnpm install
pnpm typecheck
pnpm devdocker compose up -d --buildIf your identity provider is Keycloak-compatible, you can bootstrap the Headmaster client from .env:
cp .env.example .env
./bin/headmaster env:init
python3 scripts/bootstrap-keycloak-client.py- The browser never gets direct Headscale admin credentials.
- Headscale execution defaults to
docker execagainst the configured container. - Runtime config is read from the live container environment, so Docker secrets do not need to be baked into the image.
- Policy validation works in both Docker and direct-binary modes. Policy apply supports both Headscale database mode and file-backed policy mode.
- Audit entries are written to
.data/audit.jsonl. - Release/tag workflow is documented in RELEASE.md.
- Contribution and repo policy guidance lives in AGENTS.md and CONTRIBUTING.md.
- Created by Pragith Prakash and maintained as a portable open-source project.