Your homelab cluster runs at home. You’re not always at home. You need access.
The traditional approach: forward ports, set up dynamic DNS, configure firewall rules, pray nobody finds your exposed services.
The better approach: Tailscale. Zero exposed ports. Secure WireGuard encryption. Your devices find each other, wherever they are.
What Is Tailscale?
Tailscale is a mesh VPN built on WireGuard. Every device gets a stable IP. Every device can reach every other device. No central server routing your traffic.
flowchart TD
subgraph ts["Tailscale Network (100.x.y.z)"]
Laptop["Laptop<br/>100.64.0.1"]
Phone["Phone<br/>100.64.0.2"]
Homelab["Homelab<br/>100.64.0.3"]
VPS["VPS<br/>100.64.0.4"]
end
Laptop <--> Phone
Laptop <--> Homelab
Laptop <--> VPS
Phone <--> Homelab
Phone <--> VPS
Homelab <--> VPS
Direct connections when possible. Relay through Tailscale’s DERP servers when NAT is uncooperative. Either way, encrypted end-to-end.
Why Tailscale for Homelab?
No Port Forwarding
Your router stays closed. Nothing exposed to the internet. No “I hope my SSH config is secure” anxiety.
Works Behind Any NAT
CGNAT, double NAT, hotel WiFi — Tailscale punches through. Your homelab is reachable from anywhere.
Stable IPs
Each device gets a 100.x.y.z address that never changes. No dynamic DNS headaches.
Magic DNS
Access devices by name: homelab.tail-abc123.ts.net. No remembering IPs.
ACLs
Control who can access what. The intern’s laptop doesn’t need cluster-admin.
Basic Setup
Install on Homelab Node
# Ubuntu/Debian
curl -fsSL https://tailscale.com/install.sh | sh
# Start Tailscale
sudo tailscale up
# Check status
tailscale status
Follow the authentication link. Your node is now on your tailnet.
Install on Your Devices
Install the Tailscale app on:
- Your laptop
- Your phone
- Any device that needs access
All devices authenticate to the same Tailscale account. They can now reach each other.
Verify Connectivity
# From laptop
ping homelab.tail-abc123.ts.net
# SSH to homelab
ssh user@homelab.tail-abc123.ts.net
# Access services
curl http://homelab.tail-abc123.ts.net:3000 # Grafana
No port forwarding. No firewall rules. It just works.
Subnet Router: Access Entire Network
Want to reach all devices on your home network, not just Tailscale-enabled ones?
Configure a subnet router:
# On homelab node
sudo tailscale up --advertise-routes=192.168.1.0/24
# Accept the route in Tailscale admin console
# Or use --accept-routes on clients
Now your laptop can reach 192.168.1.50 (your NAS, printer, whatever) through the homelab node.
flowchart LR
subgraph remote["Remote (Coffee Shop)"]
Laptop["Laptop<br/>100.64.0.1"]
end
subgraph home["Home Network"]
Homelab["Homelab<br/>100.64.0.3<br/>192.168.1.10"]
NAS["NAS<br/>192.168.1.50"]
Printer["Printer<br/>192.168.1.60"]
end
Laptop -->|Tailscale| Homelab
Homelab -->|Local| NAS
Homelab -->|Local| Printer
Exit Node: Route All Traffic
Working from untrusted WiFi? Route all traffic through your homelab:
# On homelab node
sudo tailscale up --advertise-exit-node
# Accept in admin console
# On laptop (when needed)
sudo tailscale up --exit-node=homelab
All your traffic now exits from your home IP. Useful for:
- Accessing geo-restricted services
- Avoiding untrusted network surveillance
- Using your home Pi-hole from anywhere
Kubernetes Integration
Tailscale Operator
The Tailscale Kubernetes Operator exposes services directly on your tailnet.
# Install operator
helm repo add tailscale https://pkgs.tailscale.com/helmcharts
helm repo update
helm install tailscale-operator tailscale/tailscale-operator \
--namespace tailscale \
--create-namespace \
--set oauth.clientId="<client-id>" \
--set oauth.clientSecret="<client-secret>"
Create OAuth credentials in Tailscale admin console first.
Expose Services
Annotate services to expose them on your tailnet:
apiVersion: v1
kind: Service
metadata:
name: grafana
annotations:
tailscale.com/expose: "true"
spec:
selector:
app: grafana
ports:
- port: 3000
Now access Grafana at grafana.tail-abc123.ts.net:3000 from any device on your tailnet.
Ingress with Tailscale
Use Tailscale as ingress controller:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: grafana
spec:
ingressClassName: tailscale
rules:
- host: grafana
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: grafana
port:
number: 3000
Access at grafana.tail-abc123.ts.net. HTTPS handled automatically.
Access Control Lists
Control who can access what:
{
"acls": [
// Admin can access everything
{
"action": "accept",
"src": ["group:admin"],
"dst": ["*:*"]
},
// Developers can access cluster services
{
"action": "accept",
"src": ["group:dev"],
"dst": ["tag:k8s:80,443,6443"]
},
// Everyone can access monitoring
{
"action": "accept",
"src": ["*"],
"dst": ["tag:monitoring:3000,9090"]
}
],
"groups": {
"group:admin": ["user@example.com"],
"group:dev": ["dev1@example.com", "dev2@example.com"]
},
"tagOwners": {
"tag:k8s": ["group:admin"],
"tag:monitoring": ["group:admin"]
}
}
Apply tags to devices in the admin console. ACLs control access.
MagicDNS and HTTPS
Automatic DNS
Every device gets a DNS name:
device-name.tail-abc123.ts.net
Configure in admin console:
- Enable MagicDNS
- Set a tailnet name (replace random string with something memorable)
HTTPS Certificates
Tailscale provides automatic HTTPS:
tailscale cert grafana.tail-abc123.ts.net
Generates valid certificates for your tailnet domains. No Let’s Encrypt, no cert-manager for internal services.
Custom Domains
Point your own domain at Tailscale:
# In your DNS
grafana.internal.example.com CNAME grafana.tail-abc123.ts.net
Then configure Tailscale to accept the custom domain.
Funnel: Public Access
Need to expose something publicly? Tailscale Funnel creates a public URL without port forwarding:
# Expose local port 3000 publicly
tailscale funnel 3000
Get a URL like https://homelab.tail-abc123.ts.net/. Useful for:
- Webhooks
- Sharing temporarily
- Demo purposes
Remember: Funnel bypasses ACLs. Anything you funnel is public.
Headscale: Self-Hosted Control Plane
Don’t want to use Tailscale’s control servers? Run Headscale:
# headscale deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: headscale
spec:
replicas: 1
selector:
matchLabels:
app: headscale
template:
spec:
containers:
- name: headscale
image: headscale/headscale:latest
args:
- serve
ports:
- containerPort: 8080
volumeMounts:
- name: config
mountPath: /etc/headscale
- name: data
mountPath: /var/lib/headscale
Full sovereignty over your mesh network. Same WireGuard protocol, self-hosted coordination.
Integration with Services
SSH via Tailscale
Tailscale can handle SSH authentication:
# Enable Tailscale SSH on server
tailscale up --ssh
# Connect (no keys needed!)
ssh user@homelab.tail-abc123.ts.net
Tailscale authenticates you. No SSH key management.
Kubectl Access
Access your cluster remotely:
# ~/.kube/config
apiVersion: v1
clusters:
- cluster:
server: https://k3s.tail-abc123.ts.net:6443
name: homelab
contexts:
- context:
cluster: homelab
user: admin
name: homelab
current-context: homelab
Works from coffee shops, hotels, anywhere.
VS Code Remote
Edit code directly on homelab:
// .ssh/config
Host homelab
HostName homelab.tail-abc123.ts.net
User youruser
VS Code Remote SSH connects seamlessly.
My Setup
My homelab Tailscale configuration:
flowchart TD
subgraph tailnet["My Tailnet"]
subgraph home["Home"]
K3s["K3s Cluster<br/>Subnet Router<br/>Exit Node"]
NAS["Synology NAS"]
end
subgraph devices["Devices"]
Laptop["Laptop"]
Phone["Phone"]
Tablet["Tablet"]
end
end
Laptop --> K3s
Phone --> K3s
Tablet --> K3s
K3s --> NAS
Configuration:
- K3s node runs Tailscale as subnet router (exposes 192.168.1.0/24)
- K3s node is exit node (for untrusted WiFi)
- Tailscale Operator exposes: Grafana, ArgoCD, GitLab
- ACLs: only my devices can access
Benefits:
- Access monitoring from anywhere
- Deploy with ArgoCD from my phone
- Never exposed a port to the internet
Common Issues
“Tailscale is blocked”
Some corporate networks block Tailscale. Options:
- Use a different port (configure DERP)
- Use Tailscale over TCP/443
“Connection is slow”
Direct connection failed, using DERP relay. Check:
- Firewall blocking UDP
- NAT type (some require relay)
“Can’t reach subnet”
Subnet routes need acceptance:
- Enable on router node:
--advertise-routes - Accept in admin console
- Enable on clients:
--accept-routes
Why This Matters
Port forwarding is a security liability. Every open port is an attack surface. Every exposed service needs hardening.
Tailscale inverts the model:
- Nothing exposed by default
- Access requires authentication
- Encryption is automatic
- Management is centralized
Your homelab stays private. You access it as if you’re home. From anywhere.
The most secure port is the one that isn’t open.
