Docker MACVLAN and IPVLAN: Giving Containers Their Own IP Addresses
By default, Docker containers run on an internal bridge network (172.17.x.x) and aren't directly accessible from your homelab network without port forwarding. For most services this is fine — you forward port 80 to your container, done. But for services that need to be "first-class" network citizens — Pi-hole, Home Assistant, a print server, or anything that needs to broadcast on the network — port forwarding becomes awkward or impossible.
Photo by Zulfugar Karimov on Unsplash
MACVLAN and IPVLAN modes solve this by giving containers their own presence on your physical network.
What MACVLAN Does
MACVLAN creates a virtual network interface for each container with its own MAC address. Your router sees each container as a separate physical device with its own IP address.
The container gets:
- Its own MAC address (randomly generated by default)
- Its own IP address (assigned by your DHCP server or statically configured)
- Full visibility on your LAN — other devices can connect directly
Use case: Pi-hole as a network-wide DNS server. By running Pi-hole in MACVLAN mode, it gets its own IP address (e.g., 10.0.0.53), and you can point your router's DNS to that IP. Pi-hole receives DNS queries directly, like a standalone device.
What IPVLAN Does
IPVLAN is similar to MACVLAN but shares the host's MAC address while giving containers unique IP addresses. The router sees the host MAC for all containers, but different IPs.
L2 mode: Similar to MACVLAN but shares MAC. Some switch environments work better with IPVLAN L2 when they restrict ARP spoofing.
L3 mode: Containers get IPs in a separate subnet, and the host routes between them. More complex, useful for specific routing scenarios.
For most homelab use cases, MACVLAN L2 is the right choice. Use IPVLAN if your managed switch or cloud environment doesn't support multiple MACs per port.
MACVLAN Configuration
Create a MACVLAN network:
docker network create \
--driver macvlan \
--subnet=10.0.0.0/24 \
--gateway=10.0.0.1 \
--ip-range=10.0.0.240/28 \
-o parent=eth0 \
macvlan-home
--subnet: your home network--gateway: your router--ip-range: the range Docker will assign IPs from (240-254 in this example — outside your DHCP pool)-o parent: your host's physical interface (eth0,ens3,enp3s0— check withip link)
In Docker Compose:
services:
pihole:
image: pihole/pihole:latest
container_name: pihole
hostname: pihole
restart: unless-stopped
networks:
macvlan-home:
ipv4_address: 10.0.0.53 # static IP for DNS
networks:
macvlan-home:
external: true
Using external: true references the network you created with docker network create. Set a static IP so you always know where Pi-hole lives.
Like what you're reading? Subscribe to HomeLab Starter — free weekly guides in your inbox.
The Host-to-Container Communication Problem
MACVLAN has an important limitation: the Docker host cannot communicate with its own MACVLAN containers. Your host at 10.0.0.10 cannot reach Pi-hole at 10.0.0.53 — they're on the same physical interface but MACVLAN isolates them.
The fix: Create a MACVLAN interface on the host that bridges back to the containers:
# Create a host-side macvlan interface
ip link add macvlan-bridge link eth0 type macvlan mode bridge
# Assign an address (use one from your ip-range)
ip addr add 10.0.0.239/32 dev macvlan-bridge
# Bring it up
ip link set macvlan-bridge up
# Add a route for the container range
ip route add 10.0.0.240/28 dev macvlan-bridge
Put this in /etc/network/interfaces or a systemd unit to persist across reboots.
Persist with systemd:
# /etc/systemd/system/macvlan-bridge.service
[Unit]
Description=MACVLAN bridge for Docker host communication
After=network.target docker.service
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/bin/bash -c 'ip link add macvlan-bridge link eth0 type macvlan mode bridge && ip addr add 10.0.0.239/32 dev macvlan-bridge && ip link set macvlan-bridge up && ip route add 10.0.0.240/28 dev macvlan-bridge'
ExecStop=/bin/bash -c 'ip link delete macvlan-bridge'
[Install]
WantedBy=multi-user.target
Pi-hole with MACVLAN: Complete Example
# Create the network
docker network create \
--driver macvlan \
--subnet=10.0.0.0/24 \
--gateway=10.0.0.1 \
--ip-range=10.0.0.240/28 \
-o parent=eth0 \
macvlan-home
# docker-compose.yml
services:
pihole:
image: pihole/pihole:latest
container_name: pihole
hostname: pihole
environment:
TZ: America/Los_Angeles
WEBPASSWORD: change-this
volumes:
- pihole-etc:/etc/pihole
- pihole-dnsmasq:/etc/dnsmasq.d
restart: unless-stopped
networks:
macvlan-home:
ipv4_address: 10.0.0.53
networks:
macvlan-home:
external: true
volumes:
pihole-etc:
pihole-dnsmasq:
Pi-hole is now accessible at 10.0.0.53. Set your router's DNS to 10.0.0.53. Done.
Multiple Services on MACVLAN
The same network can host multiple containers:
services:
pihole:
...
networks:
macvlan-home:
ipv4_address: 10.0.0.53
homeassistant:
...
networks:
macvlan-home:
ipv4_address: 10.0.0.100
avahi: # mDNS repeater
...
networks:
macvlan-home:
ipv4_address: 10.0.0.241
Each service gets a distinct IP, directly accessible from the rest of your network.
Proxmox Considerations
If your Docker host is a VM in Proxmox, MACVLAN requires the Proxmox bridge to allow MAC address spoofing. In the VM's network adapter settings in Proxmox, enable:
- Promiscuous mode: Off (not needed)
- MAC address forging: Enable (crucial — without this, MACVLAN traffic is dropped by the Proxmox bridge)
In the Proxmox UI: VM → Hardware → Network Device → Edit → check "MAC spoofing" or set the bridge to "no-mac-check".
Alternatively, use the Proxmox bridge (vmbr0) as a Linux bridge and set it to forward all traffic:
ip link set vmbr0 promisc on
IPVLAN Configuration
For environments where MACVLAN doesn't work (some cloud providers, strict switches):
docker network create \
--driver ipvlan \
--subnet=10.0.0.0/24 \
--gateway=10.0.0.1 \
--ip-range=10.0.0.240/28 \
-o parent=eth0 \
-o ipvlan_mode=l2 \
ipvlan-home
IPVLAN L2 behaves similarly to MACVLAN but uses the host's MAC for all containers. Use this if your switch or hypervisor drops MACVLAN traffic.
When to Use MACVLAN/IPVLAN
Use MACVLAN/IPVLAN for:
- Pi-hole or AdGuard Home (needs to receive DNS queries directly)
- Home Assistant (needs mDNS discovery, device detection)
- Any service that needs to broadcast or receive multicast
- Services where you want a clean IP without port translation
Use standard bridge + port forwarding for:
- Web services (nginx, caddy, apps) — port forwarding is simpler
- Most typical self-hosted applications
- Services that don't need to be "on" your network
