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:

  1. Enable on router node: --advertise-routes
  2. Accept in admin console
  3. 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.