# headscale [Headscale](https://headscale.net) — a self-hosted Tailscale control server — behind Caddy, with OIDC login delegated to [pocket-id](../pocket-id/). ## Prerequisite Register an OIDC client in pocket-id's admin UI with redirect URI: ``` https://${HEADSCALE_DOMAIN}/oidc/callback ``` Then paste its `OIDC_CLIENT_ID` / `OIDC_CLIENT_SECRET` below. ## Required `.env` values | Variable | Notes | |----------|-------| | `HEADSCALE_DOMAIN` | Public hostname (e.g. `hs.example.com`). Clients connect here over HTTPS. | | `ACME_EMAIL` | Let's Encrypt registration email. | | `TAILNET_DOMAIN` | MagicDNS suffix (e.g. `tail.example.com`). Must differ from `HEADSCALE_DOMAIN`. | | `POCKETID_DOMAIN` | OIDC issuer hostname (your pocket-id). | | `OIDC_CLIENT_ID` / `OIDC_CLIENT_SECRET` | From the pocket-id client above. | The deploy substitutes these into `config.yaml` from the embedded template. See [`.env.example`](.env.example). ## Deploy ```bash ./automations.sh # Deploy on this host → deploy: headscale ``` Or build + run the self-contained artifact: ```bash ./build.sh scp deploy.sh root@host: ssh root@host 'bash deploy.sh' # non-interactive: pass HEADSCALE_DOMAIN, ACME_EMAIL, TAILNET_DOMAIN, # POCKETID_DOMAIN, OIDC_CLIENT_ID, OIDC_CLIENT_SECRET, SKIP_PROMPTS=1 ``` Unattended provisioning: [`cloud-init.yml`](cloud-init.yml). DNS for `HEADSCALE_DOMAIN` must resolve to the host and 80/443 be reachable before deploy.