Ad Blocking + Local DNS (AdGuard Home / Pi-hole)¶
TL;DR / when to use this: Run AdGuard Home or Pi-hole as the home-network DNS: block ads and trackers network-wide, and get your own DNS server in the process. Two deployment styles — macvlan (each DNS server gets its own LAN IP, both can run at once) or bridge (one at a time, the host itself becomes the DNS server). The networking trade-offs are in Docker Networking.
Option A — macvlan (both servers, own IPs)¶
On a macvlan network, containers don't need port mappings — they're exposed directly on the LAN with static IPs:
services:
adguardhome:
image: adguard/adguardhome
container_name: adguardhome
restart: unless-stopped
volumes:
- ./work:/opt/adguardhome/work
- ./conf:/opt/adguardhome/conf
networks:
adguard_macvlan_network:
ipv4_address: 192.168.4.100
pihole:
image: pihole/pihole:latest
container_name: pihole
environment:
TZ: 'America/Denver'
FTLCONF_webserver_api_password: 'correct horse battery staple'
FTLCONF_dns_listeningMode: 'all'
volumes:
- './etc-pihole:/etc/pihole'
cap_add:
- SYS_NICE
restart: unless-stopped
networks:
adguard_macvlan_network:
ipv4_address: 192.168.4.101
networks:
adguard_macvlan_network:
driver: macvlan
driver_opts:
parent: eno1 # your host interface (find it with `ip address`)
ipam:
config:
- subnet: 192.168.4.0/24
gateway: 192.168.4.1 # your router
Pick IPs the router isn't already using.
Host ↔ macvlan communication¶
By default the host cannot talk to macvlan containers (LAN clients can). That breaks the host using its own DNS server, so create a bridge link:
sudo ip link add macvlan0 link eno1 type macvlan mode bridge
sudo ip addr add 192.168.4.254/24 dev macvlan0
sudo ip link set macvlan0 up
sudo ip route add 192.168.4.100 dev macvlan0
sudo ip route add 192.168.4.101 dev macvlan0
These are ephemeral — persist them with a oneshot systemd service:
# /usr/local/sbin/setup-macvlan.sh (chmod +x)
ip link add macvlan0 link eno1 type macvlan mode bridge
ip addr add 192.168.4.254/24 dev macvlan0
ip link set macvlan0 up
ip route add 192.168.4.100 dev macvlan0
ip route add 192.168.4.101 dev macvlan0
# /etc/systemd/system/macvlan.service
[Unit]
Description=Setup macvlan0 interface
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/setup-macvlan.sh
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
Option B — bridge (one server, host becomes DNS)¶
Simpler, no link gymnastics, but only one DNS server can bind port 53. First disable systemd-resolved's stub listener so the container can own port 53:
Then run Pi-hole (or AdGuard Home) on the default bridge network with 53:53 mapped and FTLCONF_dns_listeningMode: 'all' (required for Pi-hole on bridge).
Client notes¶
- Windows: set DNS to Automatic (
Settings → Network & internet → DNS server assignment) so the local domain resolves. - Android: set Private DNS to Automatic for local resolution.
See also¶
- Docker Networking — macvlan vs bridge in depth
- Blog posts: PiHole + AdGuard in macvlans · on the bridge network