惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园 - 叶小钗
爱范儿
爱范儿
WordPress大学
WordPress大学
Last Week in AI
Last Week in AI
博客园 - 聂微东
雷峰网
雷峰网
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
博客园 - 三生石上(FineUI控件)
T
Tailwind CSS Blog
博客园 - Franky
酷 壳 – CoolShell
酷 壳 – CoolShell
博客园_首页
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
博客园 - 司徒正美
月光博客
月光博客
大猫的无限游戏
大猫的无限游戏
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
The Cloudflare Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
人人都是产品经理
人人都是产品经理
宝玉的分享
宝玉的分享
罗磊的独立博客
Jina AI
Jina AI

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
Tailscale + k3s in a 2‑node homelab: why I use Tailscale ...
Mickaël Giro · 2026-05-27 · via DEV Community

Context

DevOps career switch in progress, minimal 2‑node k3s homelab in La Celle-Saint-Cloud. I publicly document every architecture decision — both to clarify my own choices and to show a future employer that I know how to operate a Kubernetes cluster in real conditions.

Today’s topic: how to connect 2 k3s nodes without hacking together a site-to-site VPN, without custom router tables, and without paying for a cloud.

Hardware

Two heterogeneous machines:

  • Server node: old Ivy Bridge desktop, Intel i7-3770K (2012), 32 GB RAM, RTX 2060 12 GB for future PyTorch workloads. Ethernet connection to a 1 Gb switch.

  • Worker node: GEEKOM A8 Mini PC, AMD Ryzen 7 8745HS (Zen 4, 8c/16t, 28 W TDP), 32 GB RAM, 1 TB NVMe SSD. Ethernet on the same switch.

  • Laptop: HP Pavilion Gaming running Ubuntu 24.04, my main workstation.

Both nodes run Ubuntu 24.04.4 LTS, kernel 6.17, k3s v1.35.4, containerd runtime 2.2.3.

Intentionally asymmetric topology: the server gets the GPU for ML, the worker remains a general-purpose compute node for CI/CD and stateless apps.

The problem: accessing the cluster from anywhere without exposing the LAN

When I work from home, my laptop is on the same LAN as both nodes — easy. But as soon as I move (coffee shop, trip), I want to be able to:

  • Run kubectl get pods from anywhere
  • SSH into the nodes to debug
  • Without opening port 6443 or port 22 on my home router

Three serious options:

  1. Manual site-to-site VPN like WireGuard: homegrown config, key generation, peer propagation, MTU tuning. Feasible but time‑consuming.

  2. Publicly exposed bastion: one node accessible via public SSH, the rest behind it. Increases the attack surface, not interested.

  3. Managed mesh VPN: Tailscale, Twingate, NetBird. Tailscale is free up to 100 personal devices, based on WireGuard under the hood, and installs in 5 minutes.

I chose Tailscale.

On each machine (server, worker, laptop):

curl -fsSL https://tailscale.com/install.sh | sh

sudo tailscale up --ssh

Enter fullscreen mode Exit fullscreen mode

The --ssh flag enables SSH managed by Tailscale (authentication via Tailscale ACLs rather than scattered SSH keys).

The direct 192.168.1.x:41641 is important: on the same LAN, Tailscale detects peers and does not go through DERP relays. Latency and bandwidth = native LAN. Off the LAN, it routes via DERP (Tailscale-hosted) with a bit more latency but still usable.

Immediate bonus: MagicDNS resolves ml-hellomichka and hellomichka-a8 everywhere, no more memorizing IPs.

The counterintuitive decision: Tailscale only for the control plane

The classic “Tailscale + k3s” homelab mistake you see online: forcing all Kubernetes traffic (Flannel CNI, kubelet, etcd) to go through Tailscale via --flannel-iface=tailscale0 and --node-ip=100.x.x.x.

In my setup, I chose not to do that. The proof in kubectl get nodes -o wide: the INTERNAL-IP values are my LAN IPs (192.168.1.52, 192.168.1.103), not my Tailscale IPs (100.x.x.x).

Why?

  • My two nodes are physically on the same 1 Gb switch. The Kubernetes data plane (pod‑to‑pod traffic, kubelet‑to‑API‑server) benefits from native throughput, without WireGuard overhead or reduced MTU.

  • If Tailscale goes down or Tailscale’s control plane has an incident (rare but it happens), my cluster keeps running. The decoupling of the admin control plane / K8s data plane is intentional.

  • Tailscale remains useful for what it’s best at: giving me encrypted access to my cluster from anywhere, without hacks.

Concretely, from my laptop, my ~/.kube/config points to https://100.117.114.43:6443. Port 6443 on the server is exposed only on the tailscale0 interface. Nobody on the internet can reach the API server.

The limits of this choice

To be honest, this setup has limitations you need to be aware of:

  • If I add a third node outside the LAN (at a friend’s house, on a cloud), I will have to switch to --flannel-iface=tailscale0 because the nodes will no longer be able to reach each other directly.

  • No encryption of intra‑cluster network traffic. For a personal homelab, that’s fine. For multi‑site production, it is not acceptable.

  • MagicDNS does not mix with the cluster’s CoreDNS. My pods resolve their services via Kubernetes CoreDNS, my laptop resolves hostnames via MagicDNS. No cross‑pollution, but this needs to be understood.

What’s next

Article 2 planned for next week: deploying Traefik with cert-manager to expose a service in HTTPS via an external domain, without ugly NodePorts or cloud LoadBalancers. The real question: how to publish my homelab apps on the internet cleanly when you have a dynamic residential IP.

The GitHub repo accompanying this series is coming — I did not want to publish it before having a first article setting the stage.

To follow the series: free Substack subscription. For technical feedback: LinkedIn or GitHub (link in the bio).