Docker Macvlan vs Ipvlan: Assign Dedicated LAN IPs

Updated 2026-10-20 • By Marcus Vance, CCNA (Senior Network Engineer)

To assign a dedicated local LAN IP address to a Docker container, configure a Macvlan or Ipvlan L2 network driver in docker-compose.yml bound to your host's physical Ethernet interface. This bypasses Docker's default NAT bridge, giving services like Pi-hole, Home Assistant, and Plex their own unique IP and eliminating host port collisions.

Key Technical Takeaways
  • Bypass NAT Port Collisions: Macvlan gives containers independent LAN IPs on your physical home subnet, allowing multiple containers to use Port 53, 80, or 443 simultaneously.
  • True Client Visibility: DNS servers (Pi-hole/AdGuard) running on Macvlan see exact client source IP addresses rather than Docker's masked internal gateway IP (172.17.0.1).
  • The Kernel Communication Barrier: Linux security forbids direct communication between the Docker host and Macvlan child containers; a macvlan bridge shim interface solves this.
  • Macvlan vs Ipvlan L2: Use Macvlan for distinct MAC addresses per container; use Ipvlan L2 when running on Wi-Fi NICs or cloud hypervisors that drop multiple MACs on one switch port.

The Docker NAT Bridge Bottleneck: Why Containers Need Dedicated Subnet IPs

By default, Docker deploys containers onto an internal virtual bridge (bridge or docker0) using private RFC 1918 subnets (typically 172.17.0.0/16). Communication with your physical local area network relies on Network Address Translation (NAT) and host port-forwarding mappings (e.g., -p 8080:80). While convenient for quick testing, this architecture creates severe operational hurdles for network infrastructure containers:

  • Port Collisions: Two containers cannot bind to the same host port. You cannot run Pi-hole and AdGuard Home (both requiring Port 53) or multiple web servers (requiring Port 80/443) on the same host without messy port remapping.
  • Source IP Masking: When queries hit a container behind Docker's NAT bridge, the source IP address is rewritten to the Docker gateway (172.17.0.1). In DNS sinkholes like Pi-hole, every single DNS request appears to originate from the Docker host, completely blinding your query logs and per-client blocking rules.
  • Broadcast/Multicast Failures: Services like Home Assistant, Apple AirPlay, and DLNA rely on mDNS (UDP 5353) and SSDP discovery broadcasts, which do not cross Docker's Layer-3 NAT bridge boundaries.
Diagram comparing Docker NAT bridge port forwarding against Macvlan direct local IP assignment
Figure 1: Architectural comparison between Docker default NAT bridge (left) and Macvlan direct subnet IP allocation (right).

The solution is Docker's Macvlan or Ipvlan network drivers. These drivers bind virtual container interfaces directly to your host's physical network adapter (eth0 or eno1), placing containers directly onto your physical LAN subnet with their own dedicated IP addresses.

Configuring Docker Macvlan Networks via Docker Compose

Before launching a Macvlan network, you must inspect your home router's DHCP pool. You must carve out an isolated static IP reservation range outside your router's dynamic DHCP assignment pool to avoid duplicate IP collisions. For example, if your router hands out DHCP leases from 192.168.1.100 to 192.168.1.250, reserve 192.168.1.192/27 (IPs .193 through .222) exclusively for Docker Macvlan containers.

In your docker-compose.yml file, define the Macvlan network driver under the root networks block, binding it to your host's physical NIC (e.g., eth0):

version: "3.8"

services:
  pihole:
    image: pihole/pihole:latest
    container_name: pihole
    environment:
      TZ: "America/New_York"
    volumes:
      - ./etc-pihole:/etc/pihole
      - ./etc-dnsmasq.d:/etc/dnsmasq.d
    networks:
      home_lan:
        ipv4_address: 192.168.1.201
    restart: unless-stopped

networks:
  home_lan:
    driver: macvlan
    driver_opts:
      parent: eth0
    ipam:
      config:
        - subnet: 192.168.1.0/24
          gateway: 192.168.1.1
          ip_range: 192.168.1.192/27

When you execute docker compose up -d, Docker creates a virtual interface with a randomized unique MAC address (e.g., 02:42:c0:a8:01:c9) and assigns 192.168.1.201. The container is immediately reachable across your entire physical home network on standard ports without specifying any ports: mapping lines.

Solving the Macvlan Host-to-Container Communication Lockout

Immediately after deploying a Macvlan container, you will discover a strange networking anomaly: every PC, smartphone, and smart TV on your local network can ping and access the container at 192.168.1.201, except the Docker host machine itself. Pinging the container from the host returns Destination Host Unreachable.

This is an intentional security design of the Linux kernel's Macvlan sub-driver. Traffic transmitted from a Macvlan child interface is routed directly out the physical wire; the kernel driver deliberately isolates parent interfaces from communicating with their own child sub-interfaces over Layer-2 to prevent loop storms.

To enable bidirectional communication between the Docker host and its Macvlan containers (for instance, allowing host backup scripts or metrics collectors to scrape container endpoints), you must build a macvlan bridge shim on the host OS:

# 1. Create a macvlan sub-interface linked to the physical parent
sudo ip link add macvlan-shim link eth0 type macvlan mode bridge

# 2. Assign an unused static IP from your reserved Docker subnet pool
sudo ip addr add 192.168.1.220/32 dev macvlan-shim

# 3. Bring the interface up
sudo ip link set macvlan-shim up

# 4. Route traffic targeting the Docker container range via the shim
sudo ip route add 192.168.1.192/27 dev macvlan-shim

To ensure this bridge shim persists across system reboots, place these commands into your Debian/Ubuntu /etc/network/interfaces file or create an automated systemd startup unit.

When to Choose Ipvlan L2 Over Macvlan (Wi-Fi & Hypervisors)

While Macvlan is the gold standard for wired Linux hosts, it possesses a critical physical limitation: Macvlan requires promiscuous mode on the underlying network interface because it registers multiple unique MAC addresses over a single physical link. Two primary environments will violently choke on Macvlan:

  1. Wireless Interfaces (Wi-Fi): The IEEE 802.11 standard enforces a 3-address frame format in client station mode (STA). Wireless access points drop frames originating from foreign MAC addresses transmitted over an authenticated client link. Running Macvlan over a Wi-Fi card (wlan0) almost always fails.
  2. Virtual Machines & Cloud Hypervisors: If your Docker host runs inside a Proxmox VM, ESXi, or Microsoft Hyper-V guest, the virtual switch will drop Macvlan packets by default due to MAC spoofing prevention rules unless you explicitly enable MAC Spoofing / Promiscuous Mode on that VM's virtual NIC.

In these scenarios, use Ipvlan L2. Ipvlan functions nearly identically to Macvlan by assigning unique local IP addresses to containers, but all containers share the exact same physical MAC address as the host. The Linux kernel multiplexes traffic based on destination IP instead of destination MAC. In your Compose file, simply change driver: macvlan to driver: ipvlan with driver_opts: { parent: eth0, ipvlan_mode: l2 }. Switch MAC tables remain clean, Wi-Fi links function seamlessly, and virtual machine hypervisors require zero promiscuous mode overrides.

Docker Network Drivers Architecture & Performance Matrix

Network DriverMAC Address SchemeSubnet AddressingPort Forwarding RequiredPromiscuous ModeBest For
Bridge (Default)Virtual veth pairInternal 172.17.0.0/16Yes (-p host:container)NoStandard isolated microservices, web apps
Host ModeShares Host MACIdentical to Host IPNo (Binds host stack)NoExtreme throughput (10GbE), VoIP, low latency
Macvlan (Bridge)Unique MAC per containerDirect LAN Subnet IPNo (Direct IP access)Yes (Requires wire promiscuous)Pi-hole, AdGuard, Home Assistant, multi-IP services
Ipvlan L2Shares Host MACDirect LAN Subnet IPNo (Direct IP access)NoWi-Fi Docker hosts, Proxmox/ESXi VMs without MAC spoofing
Ipvlan L3Shares Host MACRouted L3 Subnet (No ARP)No (Host acts as L3 router)NoComplex multi-subnet routed enterprise topologies
Overlay NetworkVirtual VXLANEncapsulated multi-host meshYes (Ingress mesh)NoDocker Swarm multi-node clusters across physical nodes

Docker Macvlan / Ipvlan Setup Verification Checklist

  1. Verify your home router's dynamic DHCP pool and carve out an isolated static subnet range.
  2. Identify your host's exact physical network interface name via ip -br link.
  3. Ensure promiscuous mode is enabled on upstream virtual switch if running Docker inside a VM.
  4. Define the macvlan network driver in docker-compose.yml with subnet and gateway.
  5. Deploy containers with explicit static IP assignments under the service networks block.
  6. Configure a macvlan bridge shim interface on the host OS if the Docker host requires container access.

Frequently Asked Questions

Why can't my Docker host ping its own Macvlan containers?

This is a fundamental security behavior of the Linux kernel Macvlan driver, which prevents Layer-2 hairpin loops between the parent physical interface and child virtual endpoints. To allow the host to reach its containers, you must configure a macvlan bridge shim interface on the host pointing to that subnet.

Can I run Docker Macvlan over a Wi-Fi connection?

Generally, no. The 802.11 Wi-Fi specification drops multi-MAC traffic from client adapters in standard station mode. If your Docker host connects via Wi-Fi, use the Docker ipvlan driver in L2 mode (ipvlan_mode: l2), which shares the host's single authenticated Wi-Fi MAC address across all containers.

Do I need to expose ports with -p when using Macvlan?

No. When using Macvlan or Ipvlan L2, containers behave like separate physical hardware connected to your switch. All internal container ports (e.g., 53 for DNS, 80 for HTTP, 32400 for Plex) are accessible directly at the container's assigned LAN IP without any port forwarding.

How does Macvlan benefit a Pi-hole or AdGuard Home installation?

Under Docker's default NAT bridge, all client queries appear in Pi-hole logs as originating from the Docker internal gateway (172.17.0.1). Macvlan assigns Pi-hole its own LAN IP, allowing clients to query it directly so individual client hostnames and IP addresses are cleanly tracked and filtered.

Related Wired LAN Setup & Engineering Guides

Reviewed by Marcus Vance, CCNA (Senior Network Engineer)

Part of the Packetsaver Network Engineering Team. All configurations and firmware safety instructions follow vendor-verified RFC and IEEE standards. Read our testing methodology →