The device underneath all three

Every Linux guest gets a VZVirtioNetworkDeviceConfiguration paired with a VZNATNetworkDeviceAttachment — covered in the device inventory post — which puts the guest behind NAT on a private virtual subnet, with the Mac itself acting as the NAT gateway. Every network path below is a consequence of that one fact.

Path 1: host to guest — direct, no extra step

Because the host is the NAT gateway, it can already see into the guest's private subnet. Your Mac's own browser or terminal reaches a service running in the guest by its VM IP address directly — http://<VM_IP>:<port> — with nothing to configure. This is the path used throughout the complete AI agent guide for reaching a web UI running inside the VM from the host's own browser.

Path 2: guest to the internet — transparent NAT

Outbound traffic from the guest works the ordinary way any NAT'd device's does: the guest gets a private address via DHCP, and the host's NAT translation handles everything going out. Nothing about this needs configuring either — it's the same shape as a laptop behind a home router reaching the wider internet.

Path 3: an external LAN device to the guest — this one needs a forward

This is the real gotcha, and it's worth understanding precisely rather than treating as a mystery: another machine on your LAN — a phone, a second Mac, anything that isn't the host itself — can't route directly into the guest's private virtual subnet the way the host can, because that subnet doesn't exist anywhere on the physical LAN. The fix is an explicit forward on the host, not a guest-side setting. A real, working example already used on this blog: caddy reverse-proxy --from :8081 --to <VM_IP>:4096 makes the host listen on port 8081 and forward to the guest's port 4096, so any LAN device can reach http://<HOST_IP>:8081 and land on the guest's service. The external port doesn't have to match the internal one — a genuinely useful property when the guest's own port is already claimed by something else on the host.

One more detail: the guest's address doesn't drift

Velo derives each guest's MAC address deterministically from the workspace's own ID rather than randomizing it — covered in the device inventory post — which is what lets a guest keep the same DHCP lease, and therefore the same VM IP, across restarts. Without that, the address in a Path 1 or Path 3 command above would be a moving target every time the workspace restarted.

Why Path 3 needs a forward at all: NAT-only, for now

Bridged networking — where the guest would get its own address directly on your LAN, making Path 3 as direct as Path 1 — requires the com.apple.vm.networking entitlement, which isn't yet part of the app. The Kali Linux post covers a concrete case where this actually matters: a testing toolkit built around seeing other devices on the LAN runs into exactly this NAT boundary, which a manual port forward doesn't fully substitute for.

Related reading: the Virtualization.framework device inventory, the complete AI agent guide this networking supports, and Kali Linux on Apple Silicon, where the NAT boundary matters most concretely. Or download Velo Workspaces and try it yourself.