Parent-attached modes (macvlan and ipvlan)¶
docker-net-dhcp supports three attachment modes:
| mode | how containers reach the LAN | each child's MAC | host changes required |
|---|---|---|---|
bridge |
a veth pair plugged into a Linux bridge you maintain | random per veth | yes — you bring the bridge |
macvlan |
a per-container macvlan child of one of the host's NICs | distinct (kernel-generated) | none — the host NIC is untouched |
ipvlan |
a per-container ipvlan child (L2 mode) of one of the host's NICs | shared with parent | none — the host NIC is untouched |
Picking between macvlan and ipvlan¶
Both modes attach directly to a host NIC and require no Linux bridge. The difference is at L2: each container's MAC.
macvlanis the default choice. Each container gets its own MAC, which is what most LANs and DHCP servers expect. The Fritz.Box (or any home/SOHO router) sees each container as a fully distinct device.ipvlan(L2 mode) is the right pick when the upstream switch or hypervisor refuses to bridge multiple MACs from one port. Common triggers: managed switches with sticky-MAC port-security enabled, Wi-Fi access points that refuse multi-MAC bridging, hypervisor vSwitches with strict port policies. Children share the parent's MAC; the LAN distinguishes containers by IP only.
If macvlan works on your LAN, use macvlan. ipvlan is the escape hatch for hostile L2.
Quick start¶
Create a network attached to one of the host's NICs (eth0 below —
substitute yours; ip -brief link lists them):
docker network create \
--driver=ghcr.io/claymore666/docker-net-dhcp:v1.5.0 \
--ipam-driver=null \
-o mode=macvlan \
-o parent=eth0 \
lan-dhcp
Then attach any container the usual way — no static IP, no labels, no
sidecar, no cap_add:
docker compose up -d
docker inspect app | jq '.[0].NetworkSettings.Networks'
# IPAddress is the lease your DHCP server handed out
What happens under the hood¶
docker runtriggers libnetwork'sCreateEndpointagainst the plugin.- The plugin creates a macvlan child on the parent NIC (submode = bridge, so children on the same parent can talk to each other), still in the host netns.
- A one-shot
dhcpcdruns on the new link — DHCPDISCOVER → REQUEST → ACK from your LAN's DHCP server. The lease (IP, mask, gateway) is captured. - The plugin returns the link name to libnetwork via
Join. Docker moves the link into the container's netns and renames it (typicallyeth0). - A persistent
dhcpcdruns inside the container netns to renew the lease for the lifetime of the endpoint. It runs observe-only (--noconfigure); the plugin applies lease changes via netlink. - On
docker stop, libnetwork callsLeave→ the persistentdhcpcdgetsSIGTERM→ it sendsDHCPRELEASEso the upstream server's lease table doesn't accumulate stale entries. - The macvlan link is reaped automatically when the container netns is destroyed.
The host's NIC config (IP, routes, netplan/systemd-networkd,
/etc/network/interfaces) is never touched.
Constraints¶
- The parent NIC must support macvlan/ipvlan children. Physical Ethernet, VLAN sub-interfaces, and bonds work; bridges, macvlans, and ipvlans do not (you can't stack these on top of each other).
- The parent NIC must be administratively
UPbefore you create the network — the plugin won't bring it up for you (host config is off-limits). - Like any macvlan/ipvlan setup: a container on a child interface cannot reach the parent NIC's own host IP, and vice-versa. This is a kernel-level rule, not a plugin restriction. For host↔container traffic you'd need bridge mode or a second NIC.
- ipvlan-specific: custom MAC addresses are unsupported (children
share the parent's MAC). Passing
--mac-addressondocker runwith an ipvlan network will fail withinvalid MAC address. - ipvlan-specific: only L2 mode is supported. ipvlan L3 / L3S modes are not used because they'd break DHCP (DHCP requires L2 broadcast).
- ipvlan-specific: if your DHCP server keys reservations solely
on MAC and ignores DHCP option 61 (client identifier), ipvlan
won't work as a stability mechanism — every ipvlan slave shares
the parent's MAC, so the server has no way to tell them apart.
Use
mode=macvlanif your server is MAC-only. (See "DHCP identity" below for what the plugin sends.) - ipvlan-specific: only one of macvlan or ipvlan can be active on
a given parent NIC at a time. The kernel rejects mixing them with
EBUSY. Use one mode per parent. - The plugin requires
--ipam-driver=nullbecause the LAN's DHCP server is the address source of truth, not Docker's IPAM. - One DHCP-served network per container. If a container also joins a bridge or other Docker network, that's its problem to coordinate.
Verifying¶
After creating a container on the network:
# Container's view
docker exec <container> ip -4 addr show
docker exec <container> ip -4 route show
# Host's view of the lease
docker inspect <container> | jq '.[0].NetworkSettings.Networks'
# Upstream DHCP server's view (Fritz.Box, pfSense, etc.)
# — check the active leases page in your router's UI; the container's
# MAC and hostname should appear.
A container on a macvlan should be pingable from any other host on the LAN on the IP its DHCP server handed it.
Troubleshooting¶
"parent interface is unsuitable for macvlan" — you passed a bridge,
macvlan, or ipvlan as parent. Use a real NIC, a VLAN sub-interface,
or a bond.
"ipvlan does not support a custom MAC address" — docker run --mac-address
isn't compatible with mode=ipvlan because ipvlan children share the
parent's MAC. Drop the --mac-address flag, or switch the network to
mode=macvlan if you need distinct MACs.
"parent interface is down" — ip link set <parent> up and try again.
The plugin won't toggle host link state.
Container gets no IP — check that the parent NIC is on the right L2
segment, that DHCP traffic isn't being filtered (some managed switches
have DHCP snooping or storm-control turned on), and that the upstream
DHCP server has a free lease in its pool. -o validate_dhcp=true catches
all three at network-create time instead of at the first docker run.
Everything not specific to these modes — general symptoms, the Compose merge trap, health-endpoint problems — is in the driver reference.
Where the rest of the documentation lives¶
This page covers choosing and setting up macvlan or ipvlan. The behaviour of the plugin itself is identical in every mode and is documented once, in the driver reference:
- All driver options, including
parent,validate_dhcp, and the rest - Requesting a specific address
- Restart stability — how MAC and IP survive
docker restart - DHCP identity — what the plugin sends as hostname, vendor class, and client ID
- DHCPv6
- Recovery after a plugin restart
/Plugin.Healthand the counters- Plugin settings —
LOG_LEVEL,AWAIT_TIMEOUT,STATE_DIR,OUTAGE_TICK,OUTAGE_GRACE
For how it works under the hood, see How it works.