Skip to Content
Configuration

Configuration

Headtower reads its configuration from the environment, but nothing is strictly required to boot: with no Headscale connection set, it opens a /setup wizard, and identity providers and the agent can be configured from the Settings panel once you’re in. A value you do provide is validated - a malformed URL or a half-set group (see OIDC below) is named rather than silently ignored.

These values include secrets and are read server-side only. They are never exposed to the browser, so do not prefix them with NEXT_PUBLIC_.

Required

VariableDescription
HEADSCALE_URLBase URL of your Headscale instance. Must be an http(s) URL; a trailing slash is stripped automatically.
HEADSCALE_API_KEYHeadscale API key Headtower uses to authenticate against the control plane. Grants full control-plane access - guard it.
HEADSCALE_URL="https://headscale.example.com" HEADSCALE_API_KEY="<your-headscale-api-key>"

With just these two set, Headtower runs in operator mode: whoever holds the configured API key is the single authenticated operator. There are no per-user accounts until single sign-on is configured.

Single sign-on (optional)

To put real identities in front of the console, configure OIDC. It works with any standards-compliant provider - for example Pocket ID or a generic IdP.

VariableDescription
HEADTOWER_SESSION_SECRETRequired for sign-in. Signs session cookies. Min 32 chars - generate with openssl rand -hex 32. A sign-in attempt fails without it.
HEADTOWER_OIDC_ISSUERIssuer URL of your OpenID provider.
HEADTOWER_OIDC_CLIENT_IDOAuth client ID registered for Headtower.
HEADTOWER_OIDC_CLIENT_SECRETOAuth client secret for that client.

OIDC is all-or-nothing: set all three HEADTOWER_OIDC_* variables, or none. A partial set is treated as a misconfiguration and Headtower names the gap instead of silently half-enabling sign-in. You can also add providers from the Settings panel instead of the environment - either way, set HEADTOWER_SESSION_SECRET first.

HEADTOWER_SESSION_SECRET="<32+ char random string>" HEADTOWER_OIDC_ISSUER="https://id.example.com" HEADTOWER_OIDC_CLIENT_ID="headtower" HEADTOWER_OIDC_CLIENT_SECRET="<client-secret>"

Secrets at rest (optional)

VariableDescription
HEADTOWER_SECRETEncrypts secrets (the Headscale API key, OIDC client secrets) stored in the local SQLite DB. Unset means they are stored in plaintext. Rotating it re-encrypts on the next write.

Agent (optional)

The browser SSH terminal and richer device metadata come from an optional agent sidecar, off by default (docker compose --profile agent up -d).

VariableDescription
HEADTOWER_AGENT_AUTHKEYA Headscale pre-auth key the agent registers its own node with.
HEADTOWER_AGENT_LOGIN_SERVERYour Headscale URL (required when the agent is enabled).
HEADTOWER_AGENT_URLWhere the app reaches the agent, e.g. http://agent:8410.
HEADTOWER_AGENT_SSH_SECRETShared HMAC secret for the SSH bridge; must match on both the app and the agent.
HEADTOWER_AGENT_HOSTNAMEThe agent’s node name (must match on both services; default headtower-agent).

Local state (optional)

VariableDescription
HEADTOWER_DB_PATHFilesystem path to Headtower’s own local store - operators and roles, sessions, and the audit log.

Headscale stays the source of truth for tailnet data; this store only holds Headtower-specific state. Point HEADTOWER_DB_PATH at a persistent volume so it survives restarts and upgrades:

HEADTOWER_DB_PATH="/data/headtower.db"

In a container, bind-mount the directory that holds this file (see the Docker note in Getting started). If unset, Headtower falls back to a sensible default path for local development.

A complete example

# Required - the Headscale connection. HEADSCALE_URL="https://headscale.example.com" HEADSCALE_API_KEY="<your-headscale-api-key>" # Optional - single sign-on (all three, or none). HEADTOWER_OIDC_ISSUER="https://id.example.com" HEADTOWER_OIDC_CLIENT_ID="headtower" HEADTOWER_OIDC_CLIENT_SECRET="<client-secret>" # Optional - where Headtower keeps its own state. HEADTOWER_DB_PATH="/data/headtower.db"
Last updated on