The video explains how to replace the Tailscale control server with Headscale, an open-source alternative. The author emphasizes gaining full control over the network gateway, reducing latency, and removing user limits. By self-hosting the coordination server while using the official Tailscale clients, users can create a secure, private mesh network for their home lab.
Have you seen this tweet? It's not the only one, and it got me thinking. Tailscale is amazing.
Like others, I've been happily using it forever. But its brain is still sass, so why don't I take
control over it? Especially when its open source counterpart is one of the most popular projects
on GitHub. Beyond the fact that I'll be able to get unlimited users, unlimited policies,
a fair amount of resources, which are all capped unless you pay, I'll be the one controlling
the gateway. Not a closed source server on a company I just blindly trust. When it comes to
the most sensitive pieces I have, my internal home services, it feels even more sensible.
Now, I have nothing against Tailscale, on the contrary. A fantastic service,
built on top of the amazing WireGuard, and given away for basically free. But it's not all about
money. For me, it's about latency, and control, and a bit of nerd spice that forces me to self
and run services on my own just for fun. By the end of this one, you'll have the tools to run
your own mesh network and access private services using a tunnel you dug yourself.
Let's get into it.
HeadScale does exactly that, and it's directly aimed at replacing the Tailscale control center,
while you can keep enjoying their open source client. Now, both of these are based on the
same technology, WireGuard, and sure, you can set up WireGuard yourself, but let's just say,
there's a good reason why people use Tailscale in the first place.
WireGuard is hard. Tailscale makes it unbelievably easy. But why not a VPN, you may ask? Well,
to put it simply, this is a VPN. Clients use a gateway to get access to a bunch of servers.
This can be incredibly inefficient, if you're accessing a local machine,
while you're physically not far from it, while your VPN server lives far, far away.
And if you think that's a joke, think about companies running their sensitive infra,
like VPNs on AWS US East or West, while having on-prem infrastructure in their building,
either across the country or on the other side of the globe.
And that was my situation at home. Why would I travel across continents to have my laptop
and phone securely access my security cameras? Well, WireGuard isn't forming a gateway,
but rather a mesh network of nodes. You're a machine that joined the system. You're now
part of the network infra, running lightweight WireGuard tunnels between every available pair
of nodes, making the system not only faster, but also fault-tolerant.
For Tailscale, building on top of that, there's a main service,
that's the server, helping coordinate, store private keys, etc.
Enough theory. You can read more about it in the link below. Let's get this thing running.
Now, I've mentioned I have a Homelab setup with K3S, so that's where it's going to live.
You can set it up any way you like, it doesn't really change much.
There are two important bits to the infra, the configuration and the server itself.
Starting from the first, the server URL is actually not going to stay like that. I'm
going to give it a real publicly available DNS, and we're solving the security risk here in a
really cool way. Wait for the end for that. There's an address to listen on for connections,
and another one for metrics. This is pretty much all you need to get started.
The headscale server is a container from their official Docker Hub release,
and the command is simply headscale serve. Ports are configured to match the config
we've shown earlier. An extra word about the config here, headscale implements almost
all tailscale features. There's a long list right there. One of those features is magic DNS,
which basically gives you automatic internal DNS entries for different machines. And while this is
cool, and I did configure it to play around, it's very problematic. DNS is simple, but also
complicated. If you play around with it, like setting your own adguard, or just use the magic
DNS by headscale, you'll quickly realize not all devices are happy to route you to seemingly
suspicious non-public entries. IPs work great, but who can work with these? There's a good reason
DNS was ever created. So, I've mentioned I'm using public records for mine, but in a way that
keeps the service private. More on that later. As for caddy, which is the main Homelab reverse
proxy, we'll add a simple line that sends traffic from my public domain to the local case 3s
headscale service. So, our server is up, looks like it's running and listening, so we can start
working. Time to install the tailscale client. We mentioned this bit is open source and saves us the
trouble of using anything different to create a node in the mesh network. Once it's ready,
we'll probably want to add a small UI, and while headscale doesn't come with one,
there are plenty of options on GitHub. I went with headplane, the most popular option.
Back to the server, headscale is actually its own CLI for different operations, and since it's
running inside a container, I'll run a small trick to execute in it just to fire the command. And
since I'm not going to run this type of command every single time I want something, an alias can
be helpful. Okay, let's do this. Headscale users list comes up empty, of course. Users create with
my name is pretty much enough at this point. And there I am. So, you're setting up headscale as an
alternative to tailscale, which is great if you want more control over your own network.
But once you start self-hosting more of your infra, there's one thing you very quickly realize.
Local is not the same thing as backed up. Your configs, keys, secrets, manifests, homelab nodes,
exported settings, all of these become part of the system. And if it only lives on one machine,
or one NAS, or one cluster, it's still a single point of failure. That's where internext fits
in nicely. internext is a privacy-first cloud storage platform that works really well as an
off-site backup target. It's open source, end-to-end encrypted with zero knowledge protection, GDPR
compliant. So much like the self-hosted tools we talk about on this channel, the idea is that you
stay in control of your data. internext can store it, but they can't read it. For DevOps workflows,
the big thing is our clone support. You can reconnect internext drive natively or through
webdav, which means the same automation patterns you already use still apply. Scheduled backups,
syncing config exports, pushing important files from remote machines, or just keeping a copy of
your homelab setup somewhere off-site. They've also added file versioning, which is exactly the
kind of thing you don't care about until the day you actually need it. The plan I think you should
be focusing on right now is the lifetime ultimate plan. 5 terabytes, one-time payment, no monthly
subscription. For an off-site backup target that's supposed to quietly exist in the background, that
model makes a lot of sense. Use the link in the description to check it out. The offer through
that link gives you 87% off the lifetime ultimate plan, which is higher than what's currently
available directly on the website. Now back to the video. Admin panel is ready and it needs a
key to start operating. Extract it from the container using our alias, feed it to headplane,
and there we go. No machines in the network just yet. My user is in place, time to add the first
node. Tail scale up with a custom login server, which accepts the routes and DNS entries as we've
mentioned earlier for the magic DNS. We get a URL to visit, which in turn generates a node
registration command with a user and a key. Back to the server, register the node with our user, and
boom, node macbook pro 2 is registered. Headplane is also happy and we've been given a special new
IP of an internal new network formed right now with one device. Tail scale status from my machine
and just a reminder, this is a node on the network, not the control server itself. It shows my machine
as the only one in the mesh. Now here's my idea. I know I have my laptop as a single node and my
control plane is my home server. That's cool, but now I want to add what's known in the networking
jargon as a leg in my home lab. Again, not the control server that isn't part of the game, but
rather add another node. So I went ahead and deployed a very simple tail scale container. I
called it subnet router with hopes it's one day going to literally allow some access to some
devices, but I think that's pushing the envelope of what I'm doing here. Same process. This is a long
line that gives tail scale a command to register with the network using our domain. Okay, the node
is trying to register. Now we need to visit the path in the control server. There's our command
and we're in. K3s router is ready. Over at headplane, I've added some subnets to make sure
we can actually access Kubernetes internal pod network from remote. This is our way of exposing
additional physical networks to tail scale as well and this requires approval, which is conveniently
available here on the UI. Okay, money time. I want to access Dozzle, my log service, which is also an
amazing open source and I've covered it before in a video recently on the channel. For now, let's
grab the internal K3s service and take our log view cluster internal IP that points at Dozzle. If we
did everything right, I should be able to access this IP. Boom. Dozzle is up and accessible from
outside the K3s cluster. All my application logs and monitoring is right there. Just to be extra
certain, tail scale down to kill the service and see if we can still access Dozzle. Nothing works.
Fantastic. Tail scale back up and we're back in. Okay, accessing Dozzle through the IP is going to
be impossible. In comes DNS. Now, one option I love is my local AdGuard, which is also, by the way,
accessible now with ease through HeadScale, where I can rewrite DNS entries like logs.domain and get
there fairly quickly. There's a bit of a problem with home DNS, which is why I'm reconsidering
that setup. AdGuard is fantastic for blocking crappy ads, but when you add non-standard fishy
looking entries, browsers don't tend to trust or resolve these at all. And we're not even talking
about HTTPS, that adds another layer of annoyances if you want that. So, I found something better.
I went ahead and added another public IP, but one that resolves to a private address. If you have
access, it works. But if you don't, and you shouldn't, because it's inside my K3S cluster,
or at least I hope that's the case, you have nothing to do with it. Once that's propagated
properly, my logs domain translates to Dozzle. Happy days. I gotta be honest here and say that
first, DNS is way more trickier than this and there are other options, especially for homelabbers,
which I'll explore on another video. But regardless, I now have a direct link to my
cluster applications. I do want to add, however, that setting up a single leg in the cluster and
exposing everything is a bit of a new VPN problem. The better architecture would be to deploy a small
tailscale sidecar next to the applications I actually want exposed. Let me know if that's
of interest in the comments. In the meantime, to see the basics of this homelab cluster and
how I made everything play together, check this video next. Thank you for watching, I'll see you
in the next one.