Skip to content

Native Mode

TIP

If you are looking to deploy with Docker, follow the Docker deployment guide.

Headtower can be installed and run directly on your host system without the need for Docker. This method is suitable for users who already run Headscale natively or prefer to avoid containers.

Prerequisites

  • A Linux-based operating system (e.g, Ubuntu, Debian, CentOS, Fedora)
  • Go version 1.25.1 installed (only needed to build Headtower)
  • Node.js version 22.16.x and pnpm version 10.4.x installed
  • Headscale version 0.27.0 or later installed and running
  • A completed configuration file for Headtower.

Before building and running Headtower, ensure that the directory defined in server.data_path in your configuration exists and is writable by the user who will run Headtower.

bash
# Adjust as needed and set a custom user if you desire
sudo mkdir -p /var/lib/headtower
sudo chown -R $(whoami):$(whoami) /var/lib/headtower

Building Headtower

Clone the Headtower repository, install dependencies, and build the project:

bash
# You can optionally checkout a specific release tag.
git clone https://github.com/rnihesh/headtower.git
cd headtower
pnpm install
pnpm build

Running Headtower

Running Headtower is as straightforward as running pnpm start (or also directly with node build/server/index.js). Headtower will look for a config file at /etc/headtower/config.yaml by default, but you can specify a different path by setting the HEADTOWER_CONFIG_PATH environment variable.

Ensure that the build/ directory exists relative to where the start command is run, otherwise Headtower will not be able to find the frontend assets.

Example systemd Service

Slotting this file into /etc/systemd/system/headtower.service will allow you to manage Headtower via systemd. Adjust the paths and user as needed, run sudo systemctl daemon-reload, and then enable/start the service.

ini
[Unit]
Description=Headtower Service
After=network.target # (or headscale.service if it runs via systemd)
Requires=network.target # (or headscale.service if it runs via systemd)
StartLimitIntervalSec=0

[Service]
Type=simple
User=your-username
WorkingDirectory=/path/to/your/cloned/headtower
ExecStart=/usr/bin/node /path/to/your/cloned/headtower/build/server/index.js
Restart=on-failure
RestartSec=5s

# Uncomment and set if using a custom config path
# Environment=HEADTOWER_CONFIG_PATH=/path/to/your/config.yaml

[Install]
WantedBy=multi-user.target

To access Headtower, navigate to http://localhost:3000/admin in your web browser (replace localhost with your server's IP address or domain name if not running locally).

In order to log in, you'll need to supply a Headscale API key. You can create one by running the following command within your Headscale environment:

bash
# You may want to tweak the expiration duration as needed
headscale apikeys create --expiration 90d

Enabling advanced features

You've technically completed the installation, but read on if you would like to enable advanced features like the ability to edit network settings from the UI or remote SSH from the browser.

Network Management

Network management allows you to configure Tailnet settings such as DNS servers, custom A records, the tailnet domain name, and MagicDNS from the Headtower UI.

Prerequisites

Network management (and other configurable Headscale features) requires that Headtower and Headscale both run on the same machine because Headtower needs

  • Access to read and write the Headscale configuration file
  • Access to read the /proc filesystem on Linux to locate Headscale's process

Configuration

Enabling network management is as simple as setting a few additional fields in your Headtower configuration file:

FieldDescription
integration.proc.enabledSet to true to enable process inspection.
headscale.config_pathPath to your Headscale configuration file (e.g., /etc/headscale/config.yaml).
headscale.dns_records_pathOptional. Refer to the example configuration for details.

With these settings in place, restart Headtower. You should now see additional options in the UI navbar such as "DNS" and "Settings" where you can manage your Tailnet configuration.

Remote Web SSH

Remote Web SSH allows you to open a terminal session to your Tailscale nodes directly from the Headtower web interface via Tailscale SSH. This feature requires that Tailscale SSH is running on your nodes (done via tailscale up --ssh).

This feature uses the Headtower Agent to facilitate the SSH connections. Refer to the Agent documentation for setup instructions and specifically follow the native mode configuration section to point Headtower to the correct agent location.

Single Sign-On (SSO)

Single Sign-On (SSO) authentication allows users to log in to Headtower using external identity providers such as Google, GitHub, or any provider that supports OpenID Connect (OIDC).

To get started with SSO, refer to the SSO documentation for detailed setup instructions.

Reverse Proxying

You should run Headtower behind a reverse proxy such as Nginx or Caddy in production. Additionally, putting Headscale beind the reverse proxy allows you to access both services via the same domain and TLS certificate.

Configuration

TIP

If you are using a custom path prefix for Headtower, adjust the /admin paths in the examples below accordingly.

Headscale supports integrating with several reverse proxies such as Nginx, Caddy, Apache, etc. Deploying Headtower is as simple as adding a handler to route any requests to /admin to the Headtower service. Refer to the Nginx example below for a reference configuration. A similar setup via Traefik in Docker is available in the Docker installation documentation.

Example Nginx Configuration

The following configuration will set up Nginx to proxy all Headscale requests on headscale.example.com and serve the Headtower UI under the /admin path. This is identical to how Tailscale's own admin console is served.

nginx
server {
    listen 80;
    listen [::]:80;

    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name headscale.example.com;

    # Or use LetsEncrypt with Certbot (up to you)
    ssl_certificate /path/to/your/fullchain.pem;
    ssl_certificate_key /path/to/your/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;

    location / { # Headscale runs on the root path
        proxy_pass http://localhost:8080/; # Adjust if Headscale runs on a different port
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_set_header Host $host;
        proxy_redirect http:// https://;
        proxy_buffering off;

        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        add_header Strict-Transport-Security "max-age=15552000; includeSubDomains" always;
    }

    location /admin/ { # Headtower is served under /admin
        proxy_pass http://localhost:3000; # Adjust if Headtower runs on a different port
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_set_header Host $host;
        proxy_redirect http:// https://;
        proxy_buffering off;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Custom Path Prefix

WARNING

The only officially supported path prefix for Headtower is /admin. Using a custom path prefix may lead to unexpected issues and is not recommended.

If for whatever reason you do not want to serve Headtower under /admin (e.g., you want to serve it under /headtower), you can set the prefix while building Headtower via the __INTERNAL_PREFIX environment variable.

bash
# Example for /headtower prefix
git clone
cd headtower
pnpm install
# Set the prefix here
__INTERNAL_PREFIX=/headtower pnpm build

When running Headtower, all requests will only be served under the specified path. Make sure to also adjust your reverse proxy configuration accordingly if you are using one. Additionally, if you want to change the path prefix again, you will need to rebuild Headtower with the new prefix.