Headplane
Headplane is a feature-complete web UI for Headscale, the open-source, self-hosted implementation of the Tailscale control server. It lets you manage nodes, pre-auth keys, ACLs, and DNS from a browser instead of the Headscale CLI. Headplane does not replace Headscale. It manages a running Headscale server, so this image ships both together.
Software included
Section titled “Software included”| Component | Version |
|---|---|
| Headplane | 0.7.0 |
| Headscale | 0.29.2 |
| Docker | Latest stable |
| Ubuntu | 24.04 LTS |
Headplane cannot run on its own, because every action it performs is an API call to Headscale. This image runs the Headscale control server and the Headplane UI together as a Docker Compose stack.
Requirements
Section titled “Requirements”| Resource | Minimum | Recommended |
|---|---|---|
| vCPU | 1 | 2 |
| RAM | 1 GB | 2 GB |
| Storage | 10 GB | 20 GB |
Environment variables
Section titled “Environment variables”These values are optional during marketplace deployment. Leave them blank to use the VM’s own address.
| Variable | Description |
|---|---|
HEADSCALE_SERVER_URL | Public URL your devices use to reach Headscale, such as an HTTPS domain |
HEADPLANE_BASE_URL | Public URL the Headplane UI is served from |
Set these when you front the VM with a DNS name or a TLS reverse proxy. HEADSCALE_SERVER_URL must
be an address reachable by your devices, otherwise nodes fail to register.
Getting started
Section titled “Getting started”1. Connect to your VM
Section titled “1. Connect to your VM”ssh ubuntu@<your-vm-ip>2. Wait for first-boot configuration
Section titled “2. Wait for first-boot configuration”On the first boot, a setup script writes the VM address into the Headscale and Headplane configs,
generates a session secret, starts both services, creates a Headscale user named default, and
mints an API key. This takes 1-2 minutes. Track progress:
journalctl -u headplane-first-boot.service -fThe login message (MOTD) confirms when Headplane is ready and prints the API key.
3. Retrieve the API key
Section titled “3. Retrieve the API key”The key is also written to a root-only file:
sudo cat /etc/headplane/credentials.txt4. Sign in to Headplane
Section titled “4. Sign in to Headplane”Open a browser and navigate to:
http://<your-vm-ip>:3000/adminHeadplane authenticates against Headscale with an API key. Paste the key from the previous step into the login form. The key expires after 90 days. Mint a new one at any time:
docker exec headscale headscale apikeys create --expiration 90d5. Join a device
Section titled “5. Join a device”Point any Tailscale client at your Headscale server:
tailscale up --login-server http://<your-vm-ip>:8080Approve the node from the Headplane UI, or list nodes from the CLI:
docker exec headscale headscale nodes listManaging Headplane
Section titled “Managing Headplane”Headscale and Headplane run as a Docker Compose stack in /opt/headplane.
# Check statuscd /opt/headplane && docker compose ps
# Restartcd /opt/headplane && docker compose restart
# View logscd /opt/headplane && docker compose logs -f headscaleHeadscale’s configuration is at /opt/headplane/headscale/config/config.yaml and its database under
/opt/headplane/headscale/data. ACL policies are stored in the database and remain editable from
the Headplane UI.
Security
Section titled “Security”Ports 3000 (Headplane UI) and 8080 (Headscale) are open on the VM’s network interface. UFW is enabled and allows those ports plus SSH (port 22).
Your devices must reach Headscale on port 8080, but the Headplane UI does not need to be public.
To restrict the UI to a specific IP:
sudo ufw delete allow 3000/tcpsudo ufw allow from <trusted-ip> to any port 3000For production use, point a DNS record at the VM and terminate TLS with a reverse proxy (Caddy,
nginx, or Traefik). Set HEADSCALE_SERVER_URL and HEADPLANE_BASE_URL to the HTTPS URLs, and set
cookie_secure: true in /opt/headplane/headplane/config.yaml.