Simpler Routing with Traefik and Tailscale Search Domains
Tailscale is one of my favorite piece of software, and their generous free tier makes it even more appealing.
In my homelab, I run a k3s cluster where every node is registered on Tailscale. Frankly, it is what makes the whole thing works - the entire home network is backed by WiFi (essentially an MTN router), no ethernet, so a reliable overlay network is foundational to its connectivity.
With that sorted, I needed a way to reach the services running in the cluster from any device. Tailscale provides an operator for exactly this. It adds each annotated Kubernetes service in the cluster as a device in your tailnet (fun name for your tailscale network), giving it a stable URL like service.your-tailnet.ts.net . For example, I run stirling-pdf (a self-hosted/private alternative to ilovepdf), and with TS operator, it’ll live at pdf.my-tailnet.ts.net, accessible from any of my devices, no matter where I am or what network I am connected to.
The problem though is that each exposed service adds to your device count. Tailscale’s free plan allows up to 100 devices, and burning through that with services feels wasteful, especially for a homelab.
Enter Search Domains.
Search Domains are a Tailscale DNS feature that lets you reach a host by its short name rather than its fully qualified domain. If ts.mchl.xyz is registered as a search domain, typing pdf in your browser is enough, and your browser will resolve it to pdf.ts.mchl.xyz. Tailscale appends the suffix and resolves it to the right node. By default, your tailnet domain is already in this list, which is why you can reach my-macbook instead of typing out my-macbook.your-tailnet.ts.net.
Search Domains don’t care how many services are running behind the domain, which means for the homelab, I only needed to get requests to the correct node - routing to the correct service will be handled by Traefik, something that k3s already comes with by default.
How it Works
DNS
First, add your custom domain to Tailscale’s search domain list in the admin console under DNS → Search Domains. You need a domain you control — the default ts.net won’t work since Tailscale owns it.
Then create an A record pointing to the Tailscale IP of any of the nodes running in the cluster (k3s runs Traefik as a DaemonSet so it doesn’t matter which one receives the request):
*.ts.mchl.xyz A 100.64.0.5
Using a wildcard means every new service you add is automatically covered — no extra DNS records needed.
Alternatively, you can register individual services with:
pdf.ts.mchl.xyz A 100.64.0.5
Routing with Traefik
The final glue is the Ingress resource that allows Traefik route traffic to the correct service. here’s what that looks like:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: stirling-pdf
namespace: stirling-pdf
annotations:
traefik.ingress.kubernetes.io/router.entrypoints: web
spec:
rules:
- host: pdf.ts.mchl.xyz
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: stirling-pdf
port:
number: 8080
From here, I just need to type pdf on any of my devices and that get resolved to my stirling PDF setup.
Granted, this setup won’t work for everyone. It comes with the risk of running your ingress traffic through a single node, plus if you need per-service access controls at the Tailscale layer or any of the extra features the operator offers, then it’s definitely the better tool.