You've settled on a 3-node k3s cluster. You've picked your hardware. You're ready to install. Stop. There's one thing you need to do first — and if you skip it, you'll either reinstall your ENTIRE cluster later or spend months debugging intermittent failures that look like cluster bugs but are actually IP conflicts. You need a homelab kubernetes IP plan.
Every k3s guide jumps straight to install commands. None of them tell you to plan your IPs first. But the install command requires a VIP you haven't picked yet, and three networking components need reserved IPs that must never overlap with DHCP — and kube-vip and the MetalLB pool must not overlap with each other (Traefik's IP is deliberately chosen from inside the MetalLB pool). Skip this step and you're building on a cracked foundation.
By the end of this post, you'll have an IP plan — a single page of notes with the exact IPs your cluster needs, verified against your router's DHCP range, ready to plug into the install commands in the next post. This ip planning takes 10 minutes. No special tools. Just pencil, paper, and your router's admin page.
Make sure you're following along with our Production k3s HA In Your Homelab: The Complete Architecture guide to get the full picture.
Key Takeaways
- Your k3s cluster needs three reserved IP ranges on your LAN, carved from a static block outside DHCP range: kube-vip (1 IP), MetalLB (pool), and Traefik (first pool IP).
- The k3s install command's
--tls-sanflag requires the kube-vip VIP at install time — if you don't know your VIP before you run the command, you're reinstalling later.- kube-vip and MetalLB both auto-discover network interfaces — you do NOT need to hardcode NIC names in any config. Leave
vip_interfaceblank and kube-vip finds the default route interface on its own.- This entire planning session takes 10 minutes. The IPs you write down today will be referenced in the k3s install, kube-vip deployment, and MetalLB configuration posts. One planning session, one page of notes, three posts of copy-paste config.
What IPs Does a Homelab Kubernetes Cluster Need?
Three components need reserved IPs on your LAN subnet. All three use ARP (Address Resolution Protocol), which operates at Layer 2 — same broadcast domain, same subnet as your nodes and your router. kube-vip and the MetalLB pool must not overlap with each other. Traefik's IP is deliberately chosen from inside the MetalLB pool — that overlap is by design. None can be handed out by DHCP.
Here's the lineup:
| Component | IPs Needed | Purpose | What Happens If It Conflicts |
|---|---|---|---|
| kube-vip | 1 IP | Control plane VIP that floats between nodes — this is the IP your kubectl talks to | kubectl unreachable, TLS certificate errors, cluster API dead |
| MetalLB | IP range (pool) | Provides external IPs for LoadBalancer services | Services stuck in <pending> forever, no external access |
| Traefik | 1 IP (first in MetalLB pool) | Ingress controller — HTTP/S routing into your cluster | Every ingress broken, all web UIs unreachable |
If DHCP assigns your MetalLB pool's first IP to a random device on your network — say, your kid's tablet — Traefik silently breaks. Not sometimes. Not intermittently. EVERY time that device is on. When the tablet leaves the house? Traefik magically works again. You'll waste a weekend debugging Traefik when the problem was your router's DHCP server the whole time.
I know this because I've done it. The failure is maddening because it looks like a cluster bug. It's not. It's an ARP conflict, and no amount of kubectl describe will find "your kid's tablet stole Traefik's IP" in the logs. Proper ip planning prevents this entire category of failure before you ever touch a terminal.
DHCP-assigned IPs can change. If Traefik's IP changes, every DNS record and internal reference breaks. If the kube-vip VIP changes, your kubeconfig and TLS certificate break. Static IPs aren't optional — they're the foundation everything else sits on.
How Do You Carve a Static IP Block from Your Subnet?
Pick a block of IPs at the top (or bottom) of your subnet, outside your router's DHCP range. For a /24 subnet — that's 254 usable addresses for anyone keeping score at home — a 20-IP static block is plenty.
Here's the math: DHCP range .100–.200 (100 IPs for devices), static block .201–.210 (20 IPs for cluster), remaining .1–.99 and .221–.254 for your router, switches, and future use. Your DHCP range will be different. The principle is the same.
Find Your Numbers
Step 1: Find your subnet. Run ip addr show on any node and look for the interface with that node's IP. Note the CIDR notation — probably /24, maybe /23 if you have a larger network.
Step 2: Find your DHCP range. Open your router's admin page, navigate to the DHCP server settings, and note the pool start and end addresses. Write them down.
Step 3: Pick your static block. Choose a contiguous range outside the DHCP pool — big enough for 3 components plus growth. 20 IPs is safe. 10 is the minimum. Write it down.
A Real Example
My homelab: subnet 192.168.5.0/24. DHCP hands out .100–.200. Static block .201–.210:
- kube-vip =
.201 - MetalLB pool =
.202–.210 - Traefik =
.202(first IP in the pool) - Reserved =
.211–.220(future static assignments — because there's always something)
Here's what that looks like visually:
These are the exact IPs running a cluster with 18+ apps right now. You can see them in the bootstrap README in the homelab-k8s repo. Your numbers will be different. The structure is the same.
Write this down. You'll reference these exact IPs in the k3s install post (--tls-san flag), the kube-vip deployment post (DaemonSet env vars), and the MetalLB configuration post (IPAddressPool CRD). One planning session, one page of notes, three posts of copy-paste config.
New to subnetting? The Subnet Cheat Sheet from freeCodeCamp covers CIDR notation and usable addresses in one page. But the math above is all you need for this planning step.
How Do You Prevent DHCP from Stealing Your Static IPs?
Your router must never hand out an IP from your static block. If DHCP assigns 192.168.5.202 to a random device, and that's your Traefik IP, your ingress breaks. The fix takes 30 seconds in your router's admin panel.
Router-Specific Instructions
pfSense / OPNsense: Services → DHCP Server → LAN → "Address Pool Range" → set the range to not include your static block.
OpenWRT: Network → DHCP and DNS → Static Leases. Configure the DHCP pool to end before your static block starts. Same effect.
Consumer routers (Asus, TP-Link, Netgear): LAN → DHCP Server → adjust "IP Pool End" to end before your static block starts. Most consumer routers don't have explicit exclusion fields — shrinking the pool achieves the same thing.
If You Can't Configure Exclusions
Shrink the DHCP pool. Move the end address to just before your static block. The router won't hand out those IPs because they're not in the pool. It's not elegant, but it works.
Once your DHCP exclusions are set, verify: nmap -sn <static block range> should return no results. If anything responds, that IP is in use — find it and move it before you proceed to Why k3s? Choosing and Installing k3s for Your Homelab, where you'll use the VIP you planned here as the --tls-san value.
Do You Need to Hardcode NIC Names for kube-vip and MetalLB?
Here's something nobody tells you: kube-vip and MetalLB both auto-discover network interfaces. You do not need to find your NIC name, hardcode it in any config, or worry about whether your nodes have matching interface names. The software handles it.
How kube-vip Finds Your Interface
When vip_interface is set to "" (empty string), kube-vip looks up the routing table and finds whichever interface has the default gateway. No config needed. Each node figures it out independently:
# kube-vip DaemonSet — leave blank for auto-detection
env:
- name: vip_interface
value: "" # leave blank to auto-detect the default interface
enp1s0 on a BeeLink, eth0 on a Raspberry Pi, ens18 on a Proxmox VM — all work simultaneously because each node discovers its own interface. You don't configure it. You don't even need to know the name.
How MetalLB Finds Your Interfaces
MetalLB's speaker DaemonSet runs with hostNetwork: true and announces on ALL host interfaces by default. An excludel2 ConfigMap filters out virtual and non-physical interfaces:
# MetalLB excludel2 ConfigMap — filters out virtual interfaces
apiVersion: v1
kind: ConfigMap
metadata:
name: metallb-excludel2
namespace: metallb-system
data:
excludel2.yaml: |
announcedInterfacesToExclude:
- "^docker.*"
- "^cali.*"
- "^veth.*"
- "^tunl.*"
- "^flannel.*"
- "^kube-ipvs.*"
- "^cni.*"
- "^lo$"
Everything matching those regex patterns gets skipped. Everything else — your real physical interfaces — gets announced on. No positive selection needed.
What This Means for Your Hardware
Mixed NIC names are fine. The homogeneous-hardware rule from the architecture comparison post applies to CPU architecture (all amd64 or all arm64), NOT NIC names. You can run a BeeLink, a Raspberry Pi, and a Proxmox VM in the same cluster — each finds its own interface.
The ONE thing you do need: ensure each node has a default route on a physical interface. If a node has no default gateway configured, kube-vip can't find the interface. But that's a basic networking requirement, not a Kubernetes one.
The one time you DO want to set a specific NIC: when a node has multiple physical NICs and you need to tell kube-vip which one carries the VIP traffic. In that case, set vip_interface to the interface name (e.g., eth1). For single-NIC nodes — which covers most homelab hardware — leave it blank and let auto-discovery handle it.
Every homelab guide tells you to find your NIC name and hardcode it. They're solving a problem the software already solved. Telling you what you DON'T need to do — that's the real value here.
Your IP Plan — Put It All Together
You now have everything you need for a one-page IP plan. Here's the template:
My IP Plan
----------
Subnet: _______._______._______._______/_____
DHCP Range: _______._______._______._______ - _______._______._______._______
Static Block: _______._______._______._______ - _______._______._______._______
kube-vip VIP: _______._______._______._______ (one IP from static block)
MetalLB Pool: _______._______._______._______ - _______._______._______._______ (range)
Traefik IP: _______._______._______._______ (first IP in MetalLB pool)
DHCP exclusion confirmed: [ ]
All nodes have default gateway: [ ]
Fill in the blanks. Check the boxes. Stick this on a sticky note or save it in your notes app. You'll reference it in the k3s install, kube-vip deployment, and MetalLB configuration posts — and your ip planning is done for good.
Frequently Asked Questions
Can I use a different subnet for the MetalLB pool?
No. MetalLB uses ARP (Layer 2), which requires all IPs to be on the same broadcast domain as your nodes. A different subnet requires routing (Layer 3), which MetalLB's ARP mode doesn't support. If you need cross-subnet LoadBalancer IPs, you need BGP mode — and a BGP-capable router. For homelab, keep everything on the same subnet.
What if I already installed k3s without planning IPs?
Find your node IPs (kubectl get nodes -o wide). Check your router's DHCP range. Pick an unused IP outside DHCP for kube-vip. Pick a contiguous unused range for MetalLB. If you didn't set --tls-san during install, you need to reinstall k3s on each node to add it — there's no post-install fix. Better plan: do this BEFORE installing. That's why this post exists before the k3s install post.
How many IPs do I need for a single-node cluster?
One: the node's own IP. kube-vip is pointless on a single node — there's nowhere for the VIP to float to. MetalLB can use a single IP if you only need one LoadBalancer service. But if you read the architecture comparison post, you already know why single-node isn't production homelab. Three nodes, plan your IPs. If you're not sure, plan for kube-vip anyway. If you decide to add more nodes in the future, you'll thank past you!
What's Next?
You've got your IP plan. The numbers are written down. DHCP is configured. You know your interfaces auto-discover. Now you're ready to install k3s.
Why k3s? Choosing and Installing k3s for Your Homelab walk through the exact install commands — with your VIP ready for --tls-san. No guesswork. No "pick an IP" buried in a code comment. Your IPs are planned, documented, and DHCP-proof. Let's install a cluster.
Don't be afraid to break things. Just do something.