Networking & L3 routing
Most local-Kubernetes tools hide cluster services behind port-forwards or NAT. klimax does the opposite: it routes the kind bridge subnet from your Mac into the VM at layer 3, so a pod IP is the address you connect to — and the pod sees your Mac's real source IP back.
The network stack
macOS host
bridge1xx (host IP, macOS-assigned)
│ vzNAT — Apple VZNATNetworkDeviceAttachment
▼
Lima VZ guest
lima0 (guest IP, macOS-assigned)
br-xxxx (172.30.0.1/16) ← Docker bridge "kind"
│
kind nodes (172.30.N.x)
vzNAT IPs are assigned by macOS and can't be configured. klimax detects
the VM's lima0 IP at runtime — nothing is hardcoded — so
multiple Lima VMs each get a distinct IP on their own bridge and coexist
without conflict.
How pure L3 routing works
- Host route.
klimax upadds172.30.0.0/16 → <lima0-IP>on macOS viasudo /sbin/route(this is the one-time sudo prompt). - Forwarding + SNAT exemption. Inside the VM,
ip_forward=1and a systemd oneshot apply idempotent iptables rules:- a NAT exemption (before Docker's
MASQUERADE) so kind→host traffic leaveslima0without SNAT; - DOCKER-USER forward rules that explicitly allow host→kind and established return traffic.
- a NAT exemption (before Docker's
- Survives Docker restarts. A
docker.service.ddrop-in re-runs the rules whenever Docker restarts.
The result: curl http://172.30.1.200/ on your Mac reaches the
MetalLB VIP directly, and the pod sees your real source address.
Reaching the API server: two modes
klimax supports two ways for kubeconfigs to address cluster API servers, controlled by network.disablePortMirroring.
kubectl, two address paths. The default (direct L3 to lima0) coexists with other Lima VMs; set disablePortMirroring: false to fall back to stable 127.0.0.1 loopback.Direct-IP mode (default — disablePortMirroring: true)
- Lima's TCP port mirroring is off, so klimax coexists with other Lima-based VMs (Colima, Rancher Desktop) that also manage kind clusters — otherwise both VMs race to mirror the same port (e.g.
7001) to127.0.0.1and collide. - Kubeconfigs use the VM's
lima0IP (e.g.https://192.168.64.3:700N), L2-reachable from the host via vzNAT; the API-server cert gets thelima0IP added as a SAN automatically.
Loopback mode (disablePortMirroring: false)
- API servers listen on
0.0.0.0:700Ninside the VM. - Lima's hostagent mirrors those ports to
127.0.0.1:700Non the host. - Kubeconfigs point at
https://127.0.0.1:700N— a stable address that survives VM restarts. Use it when running a single klimax VM, or when host security software (e.g. CrowdStrike) blocks direct vzNAT IPs.
disablePortMirroring is a VM-level
setting — it only takes effect on a fresh VM
(klimax destroy && klimax up). With the default, the
lima0 IP is assigned dynamically and may change on restart;
re-run klimax kubeconfig merge <name> afterwards to
refresh ~/.kube/config.
Registry traffic
Every cluster's containerd is patched to transparently cache pulls from
docker.io, quay.io, gcr.io, and Google Artifact Registry
(us-docker.pkg.dev, us-central1-docker.pkg.dev) through the pull-through
mirrors. All mirror containers join the kind Docker network,
so nodes resolve them by hostname. See
Registry mirrors.
klimax status shows the VM state, the host route, and
whether the iptables rules are present — start there when connectivity
looks off, then klimax doctor.