Headscale is an open-source and self-hosted version of the Tailscale control server. It lets you manage your own private mesh VPN network and connect devices like Linux, macOS, Android, and iPhone through your own server. In this guide, we will show you how to self-host Headscale on Ubuntu and add clients.
How to Self-Host Headscale on Ubuntu and Connect Linux, macOS, and Mobile Nodes
Table of Contents
- What Headscale Does
- What You Need to Self-Host Headscale on Ubuntu
- Method 1. Install Headscale on Ubuntu with DEB Package
- Headscale Config File
- Start and Verify the Headscale Service
- Put TLS in front of Headscale
- Create a User and Join Nodes
- Connect Linux, macOS, and Mobile Clients
- Method 2. Headscale Docker Setup
- Conclusion

What Headscale Does
Headscale gives you your own control plane for a private WireGuard mesh, so your devices can join the same network without using the default hosted control server.
It is recommended to use the DEB package path on Ubuntu because it creates a local service user, ships a default config file, and includes a systemd service file.
If you want a stable setup for remote access, self-host Headscale on Ubuntu on a VPS instead of leaving the control plane behind a home router that may change IPs or go offline. A VPS is usually a better place for the control server because it is public, always on, and easier to protect with a reverse proxy and TLS. That is why many people self-host Headscale on Ubuntu on a Linux VPS instead of a home lab box that depends on home internet uptime.
For a reliable server base, you can use a Linux VPS so your Headscale URL stays reachable all the time.
What You Need to Self-Host Headscale on Ubuntu
For this setup, you need a server running Ubuntu 22.04 or 24.04 because these versions work well with Headscale. You also need a domain name, and it should point to your server’s public IP address.
To keep the setup secure, put Nginx or Caddy in front of Headscale and use HTTPS for the public connection, since clients connect to it through a secure URL.
For a small or personal deployment, SQLite is a good choice because it is simple and easy to manage.
It is also a good idea to keep Headscale listening only on localhost and let the reverse proxy handle public traffic.
Finally, make sure ports 80 and 443 are open so the reverse proxy can serve HTTP and HTTPS traffic properly.
If you later want to publish a private app behind your VPN, read this guide on Cloudflare Tunnel for that use case.
Now proceed to the following steps to complete the setup.
Method 1. Install Headscale on Ubuntu with DEB Package
To self-host Headscale on Ubuntu, it is recommended to use the DEB package method. For installing the package, you can use the commands below:
After installation, Headscale already has a systemd unit and a default config path, which is one reason many admins self-host Headscale on Ubuntu this way instead of building the service manually.
Headscale Config File
The main config file is located under /etc/headscale/config.yaml, and it includes an up-to-date example at /usr/share/doc/headscale/examples/config-example.yaml.
When you self-host Headscale on Ubuntu, start with the shipped example for your installed version, then edit only the values that apply to your server.
Edit the config file:
A practical minimal layout looks like this:
Use your own public domain in server_url, keep listen_addr on 127.0.0.1:8080, and store the SQLite database under /var/lib/headscale/ so the service keeps its state locally.
Start and Verify the Headscale Service
After configuration changes, restart the service and verify it with the commands below:

You can also check basic health through the local service endpoint once the reverse proxy is in place or by testing the local port first.
A simple local check is:
In the output, you must see:
Put TLS in front of Headscale
Headscale works best behind a reverse proxy because your clients should connect to a clean HTTPS address rather than a local port. Open ports 80 and 443 on the firewall, keep Headscale on localhost, and let Nginx handle the public traffic.
We add HTTPS with Nginx. Use the commands below to install Nginx and Certbot:
Allow required firewall ports through the UFW firewall:
Then, make sure Headscale listens locally. Edit your config file:
Use values like these:
This keeps Headscale on localhost and lets Nginx handle TLS in front of it.
Create the Nginx site file:
Paste this config:
Enable the site and test Nginx:
Request the SSL certificate:
Certbot will request the certificate and usually update the Nginx server block for HTTPS automatically. After this, Nginx will serve your Headscale URL over HTTPS.
Once you are done, you can check everything with the following commands:
Create a User and Join Nodes
After the service starts, you can self-host Headscale on Ubuntu for a small team or personal setup by creating one user and then making a pre-auth key for node enrollment.
Connect Linux, macOS, and Mobile Clients
Next, self-hosting Headscale on Ubuntu becomes useful only after your devices join the network. You must install the Tailscale app on each device and point it to your own control server URL.
You can self-host Headscale on Ubuntu and attach a Linux machine with a pre-auth key like this:
You can also connect a Mac by using the Tailscale client with a custom control server URL, either from the app settings or the command line.
For phones, self-host Headscale on Ubuntu works best when your server has a valid HTTPS setup and a stable public domain, because the mobile app needs to reach that custom control server from any network.
After login, check that the node appears in Headscale and then test access between two joined devices.
Method 2. Headscale Docker Setup
If Docker is already installed, you can run Headscale in a container instead of using the package method. The container setup is simple, but your config file must include a DERP map and a safe DNS block, or the service may fail to start on newer Headscale versions.
First, create the folders for the config and database files:
Next, create the main config file:
Paste this working example:
This config uses SQLite for storage, disables forced DNS override, and loads the default DERP map so Headscale can start cleanly.
Now start the container:
This binds Headscale to localhost on the host machine, which is the right setup when you want to place Nginx or another reverse proxy in front of it.
After that, check that the container is healthy:

If the health check works, Headscale is running and ready for the reverse proxy and client enrollment steps.
Conclusion
Headscale is a simple way to run your own private mesh control server with Tailscale clients. With Ubuntu, SQLite, systemd, a reverse proxy, and a public domain, you can build a clean setup that is easy to manage and easy to grow later.
We hope you enjoy this guide. For more detailed information, you can check the official Headscale docs.
Yes, you should use HTTPS because clients connect through a secure control server URL.
Yes, SQLite is a good choice for a small setup and keeps the first install simple.
A VPS gives your control server a stable public IP, better uptime, and simpler firewall rules, which makes it a better option for Headscale than a typical home connection.