Kubernetes Homelab Series (Part 4): Networking and Ingress with MetalLB and Tailscale

When running Kubernetes at home or on bare metal, one of the biggest challenges is networking. How do you make your apps reachable, both inside your local network and from outside, without adding layers of complexity or security risks?

That’s where MetalLB and Tailscale come in. Together, they give you the best of both worlds:

Kubernetes Homelab Series (Part 4): Networking and Ingress with MetalLB and Tailscale

🔹 MetalLB gives your Kubernetes cluster the power to assign external IPs to services , just like a cloud load balancer, but made for bare metal. This makes your apps easily reachable within your private network (LAN), without needing any special hardware or cloud services.

🔹 Tailscale, on the other hand, helps you reach those same apps from anywhere, securely , without exposing them to the open internet. It creates an encrypted, peer-to-peer network that connects your devices and your cluster as if they were on the same local network.

👉 And that’s exactly what we’re going to set up in this new chapter of the series.

If you’re just joining in, this series documents how I built a fully functional Kubernetes cluster from scratch, turning theory into hands-on practice, one piece at a time.

Here’s a quick recap of what we’ve done so far:

Part 1: Building the cluster from scratch using a Raspberry Pi.

Part 2: Setting up persistent storage with Longhorn and MinIO.

Part 3: Adding monitoring and observability with Prometheus and Grafana.

Today, we’re enhancing our networking by deploying two essential tools: MetalLB and Tailscale. MetalLB provides easy, reliable load balancing within your private network, while Tailscale securely exposes your services remotely without direct public exposure.

1. Understanding Tailscale

Tailscale is a secure, zero-configuration VPN built on WireGuard technology, designed to simplify networking and securely connect devices and applications across the internet. It creates a mesh network where devices directly communicate over encrypted tunnels.

Key Capabilities of Tailscale:

  • Secure VPN: End-to-end encryption with WireGuard.
  • Easy Setup: Minimal configuration and rapid deployment.
  • Secure Remote Access: Safely access your Kubernetes services from anywhere.
  • Automatic Security: Handles authentication and key rotation automatically.
  • Tailscale Funnel: Publicly accessible URLs securely exposing your internal services without direct IP exposure.

Kubernetes Homelab Series (Part 4): Networking and Ingress with MetalLB and Tailscale

Why Tailscale for Kubernetes?

Exposing your Kubernetes cluster can be complex and risky. Methods like port forwarding, NodePorts, or manual VPNs often require tricky firewall configurations and introduce security concerns.

Tailscale simplifies this. It creates a secure, peer-to-peer network so you can access your apps remotely as if you were on the same LAN, no public exposure, minimal setup, and strong security across devices.

Other common approaches (and why they fall short):

  • Port Forwarding: Directly exposes your internal network, increasing risk.
  • NodePort / LoadBalancer: Often requires static IPs and detailed firewall rules.
  • Manual VPN: Secure, but usually difficult to configure and maintain.

Kubernetes Homelab Series (Part 4): Networking and Ingress with MetalLB and Tailscale

2. Deploying MetalLB

MetalLB is a powerful load balancer for Kubernetes clusters, designed to provide LoadBalancer functionality within private networks, something Kubernetes does not support natively in bare-metal or non-cloud environments.

MetalLB simplifies service exposure within the cluster by assigning internal IP addresses, streamlining traffic management, and enhancing reliability.

Diagram of MetalLB in Layer 2 mode, showing how IP traffic is routed to Kubernetes nodes

Image Source: Spectro Cloud — Getting Started with MetalLB: The Bare-Metal Load Balancer for Kubernetes

Start by deploying MetalLB using Helm:

helm repo add metallb https://metallb.github.io/metallbhelm repo updatehelm install metallb metallb/metallb --namespace metallb-system --create-namespace

Configure a pool of IP addresses (metallb-config.yaml) for MetalLB:

apiVersion: metallb.io/v1beta1kind: IPAddressPoolmetadata:  name: default-pool  namespace: metallb-systemspec:  addresses:    - 192.168.1.100-192.168.1.110---apiVersion: metallb.io/v1beta1kind: L2Advertisementmetadata:  name: l2-advertisement  namespace: metallb-system

Apply the configuration and check the pod status:

kubectl apply -f metallb-config.yamlkubectl get pods -n metallb-system

Example Output:

NAME                          READY   STATUS    RESTARTS   AGEcontroller-6c7b8f957d-2p7rw   1/1     Running   0          30sspeaker-dnqgz                 1/1     Running   0          30sspeaker-hz9qw                 1/1     Running   0          30s

3. Deploying Tailscale for Secure Ingress

Step 1: Generate and Store Tailscale Auth Key

First, generate an Auth Key from your Tailscale Admin Console to securely join the cluster to your Tailscale network.

Create a secret manifest (tailscale-auth.yaml):

apiVersion: v1kind: Secretmetadata:  name: tailscale-auth  namespace: tailscalestringData:  TS_AUTHKEY: tskey-auth-kE2dReML1511CNTRL-NL2Wg1SLa8MZBuaohnXTG

Apply the secret:

kubectl apply -f tailscale-auth.yaml

Step 2: Deploy Tailscale Kubernetes Operator

Next, generate OAuth credentials in the Tailscale dashboard (required for Ingress support). You’ll get a Client ID and Client Secret.

  • Client ID: kh5htVzE1ABCD
  • Client Secret: tskey-client-ko5htB11CNTRL-gAHCWhYhBnb9Ee

Kubernetes Homelab Series (Part 4): Networking and Ingress with MetalLB and Tailscale

Install the Tailscale Operator with Helm:

helm repo add tailscale https://pkgs.tailscale.com/helmchartshelm repo updatehelm upgrade --install tailscale-operator tailscale/tailscale-operator \  --namespace=tailscale \  --create-namespace \  --set-string oauth.clientId=your-client-id \  --set-string oauth.clientSecret=your-client-secret \  --wait

Replace the clientId and clientSecret with your actual values.

Check the operator is running:

kubectl get pods -n tailscale

Step 3: Configure Tailscale Ingress

Now let’s expose an app using Tailscale Funnel. For this demo, we’ll use Uptime Kuma, a simple and user-friendly monitoring tool. We’ll expose it securely using Tailscale ingress:

apiVersion: networking.k8s.io/v1kind: Ingressmetadata:  name: ingress-kuma  namespace: uptime-kuma  annotations:    tailscale.com/funnel: "true" # <- Here we set truespec:  ingressClassName: tailscale # <- Here we add tailscale !  defaultBackend:    service:      name: uptime-kuma      port:        number: 80  tls:    - hosts:        - uptime-kuma

Apply it:

kubectl apply -f ingress-kuma.yaml

And check the external URL:

kubectl get ingress -n uptime-kumaNAME          CLASS       HOSTS   ADDRESS                  PORTS     AGEuptime-kuma   tailscale   *       uptime-kuma.xxx.ts.net   80, 443   7m53s# check access ingresscurl https://uptime-kuma.xxx.ts.net

Conclusion

We’ve just set up MetalLB for internal load balancing and Tailscale for secure, remote access, two powerful tools that greatly simplify networking in your Kubernetes homelab. This setup improves reliability, reduces complexity, and keeps your applications accessible and safe.

There’s more to come! Stay tuned for the next posts in the Kubernetes Homelab Series, where we’ll keep exploring practical tools and techniques to level up your cluster.

If you’re working on your own Kubernetes homelab, let’s connect on LinkedIn and learn together!