Why vfkit, and not Docker, Colima, or Rancher Desktop? #
macOS cannot run Linux containers natively, so every local Kubernetes option boots a Linux VM under the hood. The trouble is where the cluster network lives and how heavy that VM is.
With the popular drivers — Minikube’s docker driver, Docker Desktop, Colima, or Rancher Desktop (which runs a Lima/Colima-style VM beneath its UI) — your nodes run inside a single, opaque Linux VM. The pod, Service, and Ingress networks exist on a bridge (docker0 and friends) inside that VM, sitting behind NAT. macOS has no route to that subnet, so a 192.168.x.x Service IP or an Ingress address is simply unreachable from your browser. The standard escape hatch is minikube tunnel or port-forwarding — extra processes that need sudo to bind ports 80/443, hang under load, and behave nothing like a real cluster.
These tools also lean toward one large, general-purpose appliance VM bundling a full container runtime and management layer. That is convenient for plain docker run, but it deepens the isolation: now your traffic is NAT’d twice (host → appliance VM → nested node container), and bridged, host-routable networking is either unavailable or off by default because it requires root.
The vfkit driver takes a different approach. It uses vfkit, a thin wrapper around Apple’s native Virtualization.framework, to launch a lightweight VM per node — no Docker Desktop, no nested containers. Paired with the vmnet-shared network, Apple’s vmnet framework hands each node a real, host-routable IP on the 192.168.64.x subnet, just like a machine on your LAN. No NAT to tunnel through, no minikube tunnel, no port-forwarding — you point your browser at the Ingress IP and it just works, exactly as it would in production.
Prerequisites #
- A Mac
- Homebrew installed.
- Terminal access with
sudoprivileges.
Step 1: Clean the Slate #
If you have tried and failed to set this up before using Homebrew’s networking tools, you must remove them. Homebrew installs these tools without the root permissions required to attach to the macOS kernel, which causes silent network failures.
Delete any hanging clusters and uninstall the Homebrew networking packages:
minikube delete
brew uninstall vmnet-helper socket_vmnetStep 2: Install the Correct Dependencies #
We will use Homebrew to install the hypervisor, but we must use the official installation script for the network helper so it properly installs as root.
1. Install the vfkit hypervisor:
brew install vfkit2. Install vmnet-helper via the official script:
curl -fsSL https://github.com/minikube-machine/vmnet-helper/releases/latest/download/install.sh | bashWhen prompted by the script — Do you want to install the sudoers rule? (y/n) — type y and hit Enter. This allows Minikube to use the network bridge without constantly asking for your password.
3. Grant manual permission (if you declined the script prompt):
vmnet-helper must run as root to create a vmnet interface. To let users in the staff group run it without a password, you must install the default sudoers rule. If you declined the automatic prompt in the step above, run this command manually:
sudo install -m 0640 /opt/vmnet-helper/share/doc/vmnet-helper/sudoers.d/vmnet-helper /etc/sudoers.d/Step 3: Configure Minikube Defaults #
To prevent Minikube from accidentally trying to wake up your VMs with the default Docker driver in the future (which will throw a GUEST_DRIVER_MISMATCH error), explicitly tell your global config to always use vfkit:
minikube config set driver vfkitStep 4: Start the Cluster (With Extra Memory!) #
This is the most critical step for Apple Silicon users. A multi-node vfkit cluster requires at least 3072 MB of memory per node. If you use the default 2048 MB, the VMs will silently crash during boot, and Minikube will incorrectly blame your firewall or bootpd for failing to assign an IP address.
Start the cluster:
minikube start --network=vmnet-shared --nodes=2 --cpus=2 --memory=3072Because we set the default driver in Step 3, we no longer need to pass the --driver=vfkit flag here!
Step 5: Verify Networking and Enable Ingress #
Check that macOS successfully handed out direct, host-routable IP addresses to your nodes:
kubectl get nodes -o wideYou should see IPs in the 192.168.64.x range instead of standard localhost IPs.
Enable the NGINX Ingress controller:
minikube addons enable ingressStep 6: Deploy a Test Application #
To prove direct routing works, let’s deploy a modern, ARM-compatible web server and route a local domain to it.
1. Deploy Alpine NGINX and expose it internally:
kubectl create deployment hello-app --image=nginx:alpine
kubectl expose deployment hello-app --port=802. Create the Ingress rule:
This tells the Ingress controller to route traffic for hello.local to our new app.
kubectl apply -f - <<EOF
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: hello-ingress
spec:
ingressClassName: nginx
rules:
- host: hello.local
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: hello-app
port:
number: 80
EOFStep 7: Update Your Mac’s Hosts File #
You need to tell your Mac’s DNS to route hello.local to the IP address of your Minikube control-plane node.
1. Find your Minikube IP:
minikube ipNote this IP — usually 192.168.64.2.
2. Edit your hosts file:
sudo nano /etc/hostsAdd this line to the bottom (replacing the IP with your actual Minikube IP):
192.168.64.2 hello.localSave and exit.
Step 8: Test the Connection #
You can now access your cluster directly without any port-forwarding!
Open your web browser and navigate strictly to: http://hello.local
🚨 Troubleshooting: The “0-Byte Plist” Bug #
If Minikube gets stuck in a loop complaining that it could not find an IP address, and you previously tried to fix it by creating a blank /var/db/dhcpd_leases file, you likely broke the macOS DHCP server.
macOS requires that file to be a valid Apple XML Property List. If it is 0 bytes, bootpd crashes entirely. Run this to inject the correct empty XML structure and restart the DHCP server:
sudo bash -c 'cat > /var/db/dhcpd_leases <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
</dict>
</plist>
EOF'
sudo killall bootpd