What Is Headscale and Why Use It?

Headscale is an open-source control server for Tailscale clients, letting you build a private VPN network without relying on anyone else’s infrastructure. You can connect your laptop, phone, and even your NAS at home—say, on a subnet like 172.16.10.0/24—securely from anywhere. This guide walks through setting it up on Ubuntu Server 24.04 with a public IP VPS from Linode or Vultr, installing it via a .deb package, securing it with NGINX, connecting clients, and routing internal subnets for remote access. It’s all about keeping control in your hands.

Headscale vs. Tailscale: The Difference

Tailscale gives you open-source clients, which is great, but its control server is closed-source and hosted by a company. That means your VPN depends on their servers, and they could start charging more or change the rules anytime. Headscale flips that—you host the server yourself on Ubuntu Server 24.04, using a VPS with a public IP. It’s fully open-source, so you’re not tied to a third party, and you can customize it however you want. For anyone serious about self-hosting, Headscale is the way to go—it’s cheaper long-term and keeps your data yours.

With Headscale, you’re not just using a VPN—you’re running the whole show on your own terms, from a $5/month VPS to your home network.

What You Need to Start

Before jumping in, here’s what you’ll need to set up Headscale on Ubuntu Server 24.04. First, a VPS with a public IP—Linode and Vultr offer solid options starting at $5/month, and that public IP is critical so your devices can reach the server from outside your network. Next, install Ubuntu Server 24.04 on that VPS; it’s stable and worked perfectly for this setup. You’ll also need SSH access to manage it remotely—most VPS providers give you this out of the box. Optionally, grab a domain like yourdomain.com to make it look clean, but the IP works too. Basic command-line skills are assumed since you’ll be running commands to install and configure everything.

  • VPS with a public IP (linode.com or vultr.com)
  • SSH to your VPS
  • A domain (e.g., yourdomain.com)

The public IP is non-negotiable—without it, your phone or laptop can’t connect when you’re on the road. Linode and Vultr make this easy. Once you’ve got that, you’re ready to build your own VPN server and route internal networks like 172.16.10.0/24 for secure access to your home devices.

Step-by-Step Setup

Step 1: Prepare the Ubuntu Server

Start by connecting to your VPS and updating it. This ensures you’re running the latest packages on Ubuntu Server , which keeps things secure and stable.

ssh ubuntu@your-vps-ip
sudo apt update && sudo apt upgrade -y

That’s the foundation—nothing fancy, just a fresh server ready for Headscale.

Step 2: Install Headscale

Headscale comes as a .deb package from GitHub, make sure you fine the latest version for their releases. — Here’s how to get it running on your Ubuntu Server 24.04 VPS.

sudo wget -O headscale.deb https://github.com/juanfont/headscale/releases/download/xx/headscale_xx_linux_amd64.deb
sudo dpkg -i headscale.deb
sudo mkdir -p /etc/headscale /var/lib/headscale
sudo nano /etc/headscale/config.yaml

In that config file, add this basic setup—replace yourdomain.com with your domain or VPS IP:

server_url: https://yourdomain.com
listen_addr: 0.0.0.0:9080
metrics_listen_addr: 127.0.0.1:9090
grpc_listen_addr: 0.0.0.0:50443
grpc_allow_insecure: true
noise:
  private_key_path: /var/lib/headscale/noise_private.key

Then start the service:

sudo systemctl enable headscale
sudo systemctl start headscale

This gets Headscale listening on port 9080. The config points clients to yourdomain.com, and the noise key secures the VPN connections.

Step 3: Secure with NGINX and HTTPS

Headscale needs HTTPS for clients to connect properly, so set up NGINX as a reverse proxy with a Let’s Encrypt certificate. This step locks down your server and makes it accessible via yourdomain.com.

sudo apt install nginx certbot python3-certbot-nginx -y
sudo nano /etc/nginx/sites-available/headscale

Add this NGINX config—again, swap in yourdomain.com:

server {
    listen 80;
    server_name yourdomain.com;
    return 301 https://$host$request_uri;
}
server {
    listen 443 ssl;
    server_name yourdomain.com;
    ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
    location / {
        proxy_pass http://localhost:9080;
        proxy_set_header Host $host;
        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;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_read_timeout 86400;
    }
}

Finish it up:

sudo ln -s /etc/nginx/sites-available/headscale /etc/nginx/sites-enabled/
sudo certbot --nginx -d yourdomain.com
sudo nginx -t
sudo systemctl reload nginx

Now Headscale runs securely on HTTPS, proxying through NGINX to port 9080.

Step 4: Connect Your Devices

With the server live, connect your devices using Tailscale clients. Here’s how to do it on another Ubuntu machine:

sudo apt install tailscale -y
sudo tailscale up --login-server https://yourdomain.com --auth-key your-auth-key

For Android or iOS, download Tailscale from the app store, set the custom server to https://yourdomain.com, and log in. If you get a registration URL (like https://yourdomain.com/register/), run this on the server:

sudo headscale nodes register --user mobile --key 

Replace with the key from the URL. This links your phone to the VPN, and you’ll see it in the node list with:

sudo headscale nodes list

Each device gets a unique IP like 100.64.0.x, forming your private network. This step connects everything—laptops, phones, whatever—securely to your Headscale server.

Step 5: Route Internal Subnets

Want to access your home NAS on 172.16.10.0/24? Advertise that subnet from a machine on your LAN (like a home server). On that machine:

sudo tailscale up --advertise-routes=172.16.10.0/24 --login-server https://yourdomain.com --auth-key your-auth-key
sudo sysctl -w net.ipv4.ip_forward=1
echo "net.ipv4.ip_forward = 1" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p

On the Headscale server, enable the route:

sudo headscale routes list
sudo headscale routes enable --route 1

Check the list again—ensure it says “Enabled: true”. If your phone can’t ping 172.16.10.x yet, add NAT on the LAN machine:

sudo iptables -t nat -A POSTROUTING -s 100.64.0.0/16 -o eth0 -j MASQUERADE
sudo apt install iptables-persistent -y
sudo iptables-save > /etc/iptables/rules.v4
sudo systemctl restart tailscaled

That restart was the fix for me—routing kicked in after it. Now your phone can ping 172.16.10.50 (or whatever’s on that subnet), giving you secure access to your NAS from anywhere.

Why This Setup Works

This Headscale setup on Ubuntu Server with a Linode or Vultr VPS gives you a private VPN without third-party servers. You control it all—the server, the clients, the subnets. It’s cost-effective—a $5/month VPS handles it—and secure, with HTTPS via NGINX and your own keys. Routing internal subnets like 172.16.10.0/24 means your home network is always accessible, whether you’re at a café or across the globe. Plus, it’s open-source, so no company can pull the rug out from under you.

  • Full control with your own server
  • Secure access to 172.16.10.0/24 from anywhere
  • Cheap VPS setup with Linode or Vultr
  • No third-party risks

Extra Tips

If something’s off, check logs on the server with sudo journalctl -u headscale -f or restart services. The NAT and systemctl restart tailscaled were clutch for subnet routing—don’t skip them if pings fail. For more devices or subnets, repeat the client and routing steps. Headscale scales easily. You’ve got a VPN that’s all yours—tweak it, secure it, own it.

Also: headscale.net has more docs if you need them


Leave a Reply

Your email address will not be published. Required fields are marked *