Macvlan private mode for container L2 isolation
Macvlan is a Docker networking mode that gives each container its own MAC and IP on the network the host port is plugged into. The container shows up on the subnet as its own device, gets a DHCP lease from the router, and talks to the LAN directly instead of going through the Docker bridge.
I have a box with multiple Ethernet ports, each port on a different VLAN. Some containers sit on the port assigned to a guest-style VLAN that can only reach the internet, not other hosts on the LAN. That worked fine in the default bridge macvlan mode while each port only had one service on it. Once I added more services to the same port, I hit a problem I hadn’t thought about: containers sharing a macvlan parent in bridge mode can freely ARP and talk to each other inside the kernel, which defeats the VLAN-level isolation.
Macvlan has a private mode for exactly this. The kernel drops frames going between siblings on the same parent. Outbound traffic via the gateway still works. No switch cooperation, no extra VLANs.
docker network create -d macvlan \
--subnet=192.0.2.0/24 \
--gateway=192.0.2.1 \
-o parent=eth0 \
-o macvlan_mode=private \
guest_private
Attach a container to it with a static IP:
services:
app:
networks:
guest_private:
ipv4_address: 192.0.2.16
networks:
guest_private:
external: true
One caveat: the host itself can’t reach its own containers over that interface. That’s true for any macvlan setup, not specific to private mode. For my case it’s fine since the containers just need internet access and to be reachable from other subnets through the router.
More on the kernel feature underneath: Linux kernel macvlan docs. Docker’s guide: Docker macvlan driver.