Tailscale is one of those magical technologies that I can no longer imagine living without. Its use cases range from homelab setups to enterprise workloads spread across complex, disparate environments. At exe, we use it to connect much of our internal infrastructure.

That said, there are points of friction. One of my pet peeves is having to switch tailnets whenever I want to briefly connect to Home Assistant or Frigate to see who’s at the door while working on exe’s infrastructure. Tailscale’s fast user switching helps, but switching still disconnects one tailnet before connecting the other. That disrupts existing connections, takes a few seconds, and requires me to remember to switch back.

Why can’t I just be connected to multiple tailnets at the same time?

A few technical complications make it tricky, starting with the fact that Tailscale allocates IPv4 addresses independently within each tailnet. This means two nodes in two different tailnets can end up with the same IPv4 address. If both tailnets were connected simultaneously, the address alone wouldn’t tell the client which node I intended to reach.

I could disable IPv4 and use IPv6 exclusively, but that still doesn’t work everywhere. It’s getting better, but the long tail is, well, long. Disabling IPv4 requires updating the Tailscale policy file and passing some pain along to my teammates, which feels unjustified.

Node sharing has similar challenges. Tailscale does the work of making sure those IPs don’t collide, but it requires that I be an admin on both tailnets, and I’d have to individually share every device I wanted to reach. As my kid is fond of saying: I don’t want to!

A couple of weeks ago, I wondered whether I could have an agent write me a new Tailscale client that would let me connect to multiple tailnets at once. As usual, I spun up a new VM on exe and told Shelley about my problem, and we went back and forth on the design until I was convinced the solution would work for the vast majority of use cases.

The solution was fairly straightforward: spin up a couple of tsnet.Server instances, put them behind a single TUN interface, assign each peer node in each tailnet a new IPv4 from a locally configured pool such as 10.58.0.0/16, and hijack MagicDNS to return those IPs. Traffic sent to one of them is then routed through the corresponding tailnet.

It worked the first time I tried it. All of the Tailscale policies worked just as they should, and I didn’t have to implement any of that, as the upstream tsnet.Server is a full-fledged Tailscale client already.

The result is tailmix, an independent, open-source client that lets one machine connect to multiple tailnets simultaneously. The source, installation instructions, and current limitations are all in the repository—or you can ask Shelley to tell you all about it.

I still have two tailnets, two identities, and two separate sets of policies. The difference is that now my laptop no longer makes me choose between them.