Source: zakaria - Published on 2022-02-15 and updated on 2025-11-20

Note: This guide is severely out of date. The author recommends using ssl-proxy instead of relayd due to changes in the Tailscale protocol regarding web-sockets.

In this post I’ll detail the steps I took to install and configure headscale, an open-source self-hostable implementation of the Tailscale control server, on OpenBSD.

Code blocks prefixed with # imply that the command should be run as a privileged user/root.

Compilation

Since my server is on OpenBSD-stable, headscale is only available via pkg_add on -current, I need to compile the headscale binary by hand and upload it to the server.

$ git clone git@github.com:juanfont/headscale.git
$ cd headscale
$ make generate
$ make build
$ sftp user@server <<EOF
> put ./headscale
> bye
EOF

Then login, get root access, and copy the binary to /usr/local/bin:

$ ssh user@server
$ doas -s
...
# cp ./headscale /usr/local/bin
# chown root:bin /usr/local/bin/headscale

Configuration

Next we need to setup:

  1. A _headscale daemon user that headscale will run as.
  2. Directories for headscale to store its sqlite database, private key, and socket (making sure they have the correct permissions/owner).
  3. Copy the example config from GitHub, and edit it to our liking.

First we setup some directories:

# mkdir -p /etc/headscale
# touch /etc/headscale/config.yaml
# mkdir -p /var/headscale

Then we add our _headscale daemon user, and chown all the necessary dirs.

# useradd -L daemon -s /sbin/nologin -d /var/headscale _headscale
# chown -R _headscale:_headscale /var/headscale 
# doas -u _headscale /bin/ksh
$ touch /var/headscale/db.sqlite

Finally we can edit the headscale config. I highly recommend copying the example config and using that as a starting point.

# vi /etc/headscale/config.yaml
# # copy the example config
# # change domain, IPs, ports
# # change sock location to /var/headscale/headscale.sock 

Run headscale serve to see if the config works, and all the directories and permissions are correct:

# doas -u _headscale headscale serve
...

rc.d

I’ve made an init script to making stopping/starting and running at boot a lot easier. Drop this simple script into /etc/rc.d/headscale:

#!/bin/ksh
#
# /etc/rc.d/headscale

daemon_user="_headscale"
daemon="/usr/local/bin/headscale"
daemon_flags="serve"

. /etc/rc.d/rc.subr

rc_cmd $1

chmod, enable and start it as usual:

# chmod +x /etc/rc.d/headscale
# rcctl enable headscale
# rcctl start headscale
headscale(ok)

Clients

OpenBSD client

It’s pretty easy to get the Tailscale client on OpenBSD to use your control server.

# pkg_add tailscale
# rcctl enable tailscaled; rcctl start tailscaled
# tailscale --login-server "https://headscale.example.com:443"
...

Android client

The Android client supports custom Control Server URLs out of the box now. For historical purposes, instructions for patching and building a custom APK can be found here.