If you've ever set up Tailscale, you already know the appeal: no port forwarding, no static IP, no fighting with your router's firewall. You install it on two devices, sign in, and they can talk to each other over an encrypted WireGuard tunnel no matter where they physically are. It's genuinely one of the best quality-of-life tools in the self-hosting world.

What a lot of people don't realize is that the "magic" behind Tailscale is really just a coordination server — something that helps your devices find each other and exchange encryption keys, then gets out of the way. Tailscale Inc. runs that coordination server for you, for free up to a point, then charges once you pass certain limits. Headscale is an open-source reimplementation of that exact coordination server, and you can run it yourself on hardware you already own. That includes a Proxmox VE box sitting in your closet.

This guide covers installing Headscale inside a small LXC container on Proxmox VE, configuring it, and connecting your first Tailscale client to it. By the end you'll have your own private VPN mesh network with zero reliance on Tailscale's cloud infrastructure.

What You Will Learn

  • What Headscale actually is and how it's different from Tailscale itself
  • Why you'd want to self-host your VPN control server instead of using Tailscale's free tier
  • How to spin up a dedicated LXC container for Headscale using the Proxmox VE Community Scripts helper
  • How to configure the server_url and get the service running correctly
  • How to create a user, generate a pre-auth key, and register your first device
  • Common errors you'll hit along the way and how to fix them

What Is This Feature?

Let's untangle a few terms before we touch a keyboard, because this is where most beginners get lost.

WireGuard is a modern VPN protocol built into the Linux kernel. It's fast, simple, and handles the actual encrypted tunnel between two devices. On its own, though, WireGuard requires you to manually configure every peer's public key and IP address — tedious once you have more than two or three devices.

Tailscale sits on top of WireGuard and automates all that manual key exchange. Your devices each run the Tailscale client, check in with a central coordination server, and get handed the information they need to connect directly to each other. That coordination server is the part most people never think about, because Tailscale Inc. hosts it for you.

Headscale is an open-source, community-maintained clone of that coordination server. It speaks the same protocol as Tailscale's official control server, which means the regular Tailscale client — the same app you'd install from tailscale.com — can be pointed at your own Headscale server instead of Tailscale's. Your devices, your keys, your infrastructure.

One more term, since this is a Proxmox tutorial: an LXC container is a lightweight, OS-level virtual machine. Instead of emulating an entire computer like a full VM does, it shares the host's Linux kernel and just isolates the processes, filesystem, and network. That makes it faster to start, lighter on RAM, and a good fit for a small service like Headscale that doesn't need much horsepower.

Why Would You Use It?

Tailscale's free tier is genuinely generous, so it's fair to ask why you'd bother self-hosting the control plane at all. A few reasons come up constantly in the Proxmox and homelab communities:

  • Device limits. Tailscale's free plan caps you at a certain number of devices and one user. Once you start adding cameras, sensors, a few VMs, a phone, a laptop, and a couple of friends' devices, you'll bump into that ceiling fast.
  • Data sovereignty. Even though Tailscale doesn't route your actual traffic through its servers (connections are peer-to-peer whenever possible), the coordination metadata — who's on your network, device names, IP assignments — still lives on their infrastructure. Some people would rather that stayed on hardware they control.
  • No account dependency. Your private network doesn't go down or get locked because of a billing issue or an account flag on someone else's platform.
  • It's a fun, genuinely useful thing to run. Honestly, a lot of homelabbers do this simply because they can, and because understanding how the control plane works makes you better at troubleshooting VPNs in general.

That said, I'll be upfront: Headscale isn't officially affiliated with or supported by Tailscale Inc., and it doesn't implement every feature of the commercial product. Things like MagicDNS, some ACL edge cases, and the full admin console have historically lagged behind or worked a little differently. For a homelab or small team, none of that tends to matter much.

Prerequisites

Before you start, make sure you have:

  • A working Proxmox VE host (this guide was written against Proxmox VE 9.2, but it works fine on 8.x too)
  • Root access to the Proxmox shell, either through the web UI's >_ Shell button on your node, or SSH
  • A basic understanding of how to open a terminal and run commands — you don't need to be a Linux expert
  • Ideally, a domain name you can point at your Headscale server. You can absolutely use a raw IP address to start, but you'll want a proper hostname before you rely on this long-term (more on why in Best Practices below)
  • The Tailscale client already installed on whatever device you plan to connect first — grab it from tailscale.com if you haven't

Step-by-Step Tutorial

1. Create the LXC container

The fastest and most reliable way to get Headscale running on Proxmox VE is through the Proxmox VE Community Scripts project — a large, actively maintained collection of install scripts that build a properly sized LXC container and install the software for you. It's not an official Proxmox product, but it's widely trusted in the community and saves you from hand-building the container.

Log into your Proxmox VE web interface, click on your node, and open the >_ Shell. This runs commands on the Proxmox host itself, not inside a container, which matters here — you're about to create the container, so there's nothing to log into yet.

Paste in:

bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/headscale.sh)"

The script will ask a couple of quick questions (or you can accept the defaults) and then build the container for you. Out of the box it creates a Debian 13 container with 1 CPU core, 512 MB of RAM, and a 2 GB disk. That's genuinely enough for Headscale — it's a small Go binary with modest resource needs, even with a few dozen devices connected. If you're planning to run this for a large family or a small business with lots of devices, bump the RAM to 1 GB just to have headroom; you can do that later from the container's Resources tab without reinstalling anything.

Once the script finishes, it prints the container's IP address. Write that down — you'll need it in the next step.

2. Find your container and confirm it's running

In the Proxmox web UI, you should now see a new container in the left-hand tree, usually named headscale. Click it, and check the Summary tab to confirm its status is green and running.

Open a console session to it (click >_ Console) so you can run the configuration commands in the next steps directly inside the container.

3. Set your server_url

Headscale's main configuration file lives at /etc/headscale/config.yaml. The single most important setting in there is server_url — it tells every client exactly where to find the server, and it gets baked into how devices register. Get this wrong at the start and you'll be re-registering devices later.

Open the file with a text editor:

nano /etc/headscale/config.yaml

Find the server_url line near the top and set it to your container's IP for now (you can move to a domain name later):

server_url: http://192.168.1.50:8080

Swap in your actual container IP. Save with Ctrl+O, then exit with Ctrl+X.

4. Restart the Headscale service

Any time you edit the config file, you need to restart the service for the change to take effect:

systemctl restart headscale

Then confirm it came back up cleanly:

systemctl status headscale

You're looking for active (running) in green. If it says failed instead, jump down to the Troubleshooting section — nine times out of ten it's a YAML indentation mistake in the config file.

5. Create your first user

Headscale organizes devices under users (sometimes called namespaces in older versions). Create one for yourself:

headscale users create myself

You can name it anything — it's just a label used to group your devices, not a login account with a password.

6. Generate a pre-auth key

To connect a device, you need a pre-authentication key. Think of it as a one-time (or reusable, if you choose) password that lets a Tailscale client register itself against your server without you manually approving it from a web UI every time.

headscale preauthkeys create --user myself --expiration 24h

This prints a long key string. Copy it — you'll paste it into the Tailscale client in a moment. By default, a key is single-use, so if you're planning to add several devices at once, add the --reusable flag.

7. Connect a Tailscale client

On the device you want to connect (your laptop is the easiest to test with first), run:

tailscale up --login-server http://192.168.1.50:8080 --authkey tskey-auth-xxxxxxxxxxxx

Replace the login-server address with your Headscale server's server_url, and the authkey with the one you just generated. If everything's configured correctly, the command returns almost instantly with no errors, and the device is now on your private network.

8. Verify the connection

Back on the Headscale container, list connected nodes:

headscale nodes list

You should see your device listed, with an assigned Tailscale IP (something in the 100.x.x.x range) and a recent "last seen" timestamp. That's it — you're running your own VPN control server.

Commands Explained

CommandWhat it does
headscale users create <name>Creates a new user (a grouping label for devices) on the server
headscale preauthkeys create --user <name>Generates a key a device can use to register itself automatically
headscale nodes listShows every device currently registered, its IP, and when it last checked in
tailscale up --login-server <url> --authkey <key>Points the Tailscale client at your Headscale server instead of Tailscale's own and registers the device
systemctl restart headscaleReloads the Headscale service so config file changes take effect
journalctl -u headscale -fStreams live log output from the Headscale service, useful when something's not connecting

Common Errors

"failed to authenticate key" when running tailscale up. Almost always means the pre-auth key already expired or was already used once (and you didn't pass --reusable when creating it). Generate a fresh one.

Client hangs on "Connecting to Headscale" and eventually times out. The device usually can't reach the server_url address at all — check that the IP is correct, that the container is actually running, and that nothing on your network (a VLAN, an isolated bridge, a firewall rule) is blocking the connection.

systemctl status headscale shows "failed" immediately after editing config.yaml. This is a YAML syntax problem nearly every time — usually a tab character where a space should be, since YAML is picky about indentation. Run journalctl -u headscale -n 50 to see the exact parsing error, which will point at the offending line.

"context deadline exceeded" in the logs. Typically a DNS resolution issue if you've set server_url to a domain name rather than an IP and that domain isn't resolving yet.

Troubleshooting

Start with the logs. Headscale runs as a systemd service, so journalctl -u headscale -f (the -f follows the log in real time) will show you exactly what's happening as a client tries to connect. Most connection problems show up here immediately, with a specific error rather than a vague failure.

If the container itself seems unreachable, confirm basic networking first. From another machine on your LAN, try ping against the container's IP. If that fails, the problem is Proxmox networking, not Headscale — check that the container's bridge (usually vmbr0) matches the rest of your LAN, and that the container picked up a valid IP.

If devices connect but can't actually reach each other, that's usually a NAT traversal issue rather than a Headscale problem. Headscale, like Tailscale, tries to establish a direct peer-to-peer connection and falls back to a DERP relay server when that's not possible. Running your own DERP server is a more advanced topic outside the scope of this guide, but for most home networks the public Tailscale-operated DERP relays work fine as a fallback even when you're using your own Headscale control server.

One habit that saves a lot of headaches: after any config change, run headscale configtest before restarting the service. It validates the YAML and tells you exactly what's wrong instead of leaving you to guess from a failed systemd unit.

Best Practices

Switch to a real domain name before you rely on this for anything important. If your Headscale container ever changes IP — a DHCP lease expires, you rebuild the container, you move it to another node — every device that registered against the old server_url will need to be reconfigured. A domain name pointed at the container sidesteps that entirely.

Put a reverse proxy with a real TLS certificate in front of Headscale once you're past the testing stage. Running everything over plain HTTP on your LAN is fine while you're getting familiar with it, but if you ever plan to connect a device from outside your home network, you want that traffic encrypted. Nginx Proxy Manager or a Let's Encrypt setup on the container itself both work well for this.

Back up two things regularly: /etc/headscale/config.yaml and the SQLite database (by default at /var/lib/headscale/db.sqlite). Together they're the entire state of your VPN — every registered device, every user, every key. Losing them means re-registering every single device from scratch.

Keep pre-auth keys short-lived unless you specifically need a reusable one for provisioning a batch of devices. A key that never expires sitting in a script or a note somewhere is a standing risk.

Update the container periodically. The community-scripts helper that built your container also handles updates — run the same install command again from the Proxmox shell and it detects the existing installation and updates it in place rather than creating a duplicate.

Frequently Asked Questions

Is Headscale officially made by the Tailscale company?

No. It's an independent, open-source project that implements the same coordination protocol so the official Tailscale client can talk to it. Tailscale Inc. doesn't maintain or officially support it.

Do I need a public IP address to use Headscale?

Not for devices on the same LAN as your Proxmox server. If you want to connect a device while you're away from home, you'll need either a public IP with the port forwarded, or a reverse tunnel service pointed at your container.

Can I run Headscale and still use regular Tailscale on other devices?

Not on the same device at the same time — a Tailscale client can only be logged into one control server at once. But you can absolutely have some devices on your Headscale network and others on Tailscale's own service, as long as they're separate machines.

Does Headscale support MagicDNS?

Yes, current versions support Headscale's own implementation of MagicDNS, letting you reach devices by name instead of their 100.x.x.x IP address. It's configured in the same config.yaml file under the dns section.

What happens if my Proxmox host goes offline?

Your devices lose the ability to register new peers or receive updates, but existing peer-to-peer connections between already-connected devices typically keep working until they need to re-negotiate. It's still a single point of failure worth keeping in mind if you depend on this network daily.

Conclusion

Running Headscale on Proxmox VE is a small time investment for a genuinely useful result: a private VPN mesh you fully control, with no device caps and no dependency on someone else's servers staying up. The community-scripts install handles the tedious container setup, which leaves you to focus on the part that actually matters — the config file, your users, and getting that first device connected.

From here, the natural next steps are putting a proper domain and TLS certificate in front of it, and looking into ACLs if you want tighter control over which devices on your network can talk to each other. Both are worth doing once you've confirmed the basics work, which you now have.