I have recently been able to run PiHole on OpenWrt x86 using Docker. Before OpenWrt I had an Ubuntu Docker host which was running the PiHole container. The PiHole container was attached to the LAN network using Linux/Docker macvlan. Also for the Ubuntu Host to be able to ping the PiHole container, a workaround posted on stackoverflow was applied which creates a linux macvlan that the container uses. Giving PiHole an IP Address different than the IP Address of the host greatly simplifies the setup and prevents any conflict with DNS/DHCP/HTTP ports overlapping.
The idea for setting this up on OpenWrt is very similar and so would like to share it with rest of the community and get some feedback. Bear in mind that this was tested on OpenWrt x86 and RPi (thanks to @Deuxst) and may differ or otherwise be unsupported for other targets. The goal of this tutorial is to setup PiHole as an AdBlock DNS server only without using it's DHCP capabilities (DHCP is handled by OpenWrt).
Warning: The tutorial has been worked on IPv4 LAN setups, and has not been made to support IPv6. For it to work, the IPv6 DHCP settings need to be disabled which are shown as part of the tutorial. (If anyone would like to figure out how to make it work with IPv6, I'd be glad to update the tutorial, though we might need to consider having both options IMO)
uci -q delete dhcp.lan.dhcpv6 uci -q delete dhcp.lan.ra uci -q delete network.lan.ipv6 uci commit /etc/init.d/odhcpd stop /etc/init.d/odhcpd disable /etc/init.d/network restart
The LAN should look something like the below where IPv6 is not showing anymore:
opkg update && opkg install docker luci-app-dockerman docker-compose dockerd kmod-macvlan reboot
Here the MACVLAN interface will be created which is a virtual interface bridged to current LAN interface. This interface will later be used by Docker and PiHole.
Look for the
config interface 'lan' section and note down the device from the
option device line. In my case it is
Add the following at the end of the file. The key part here is
option device and
option name which is the same device as in the
lan section with
config interface 'macvlan' option proto 'static' option defaultroute '0' option netmask '255.255.255.255' option device 'br-lan.20' option ipaddr '192.168.1.2' config device option type 'macvlan' option ifname 'br-lan' option mode 'bridge' option name 'br-lan.20' option acceptlocal '1' config route option interface 'macvlan' option target '192.168.1.3' option netmask '255.255.255.255'
Look for section similar to below:
config zone option name 'lan' option input 'ACCEPT' option output 'ACCEPT' option forward 'ACCEPT' list network 'lan'
Then add the following line to it:
list network 'macvlan'
/etc/init.d/network restart /etc/init.d/firewall restart
cd ~ vi docker-compose.yml
Copy and paste the following and save. The following was retrieved from PiHole docs and slightly modified to fulfill our purpose. In the
netowrk section at the bottom, the
parent is set to the new
macvlan interface created in the previous step from the
option device line (the one with suffix
.20). Also, the PiHole container will have a static IP which I've set to 192.168.1.3.
version: "3.3" # More info at https://github.com/pi-hole/docker-pi-hole/ and https://docs.pi-hole.net/ services: pihole: container_name: pihole image: pihole/pihole:2021.09 hostname: pihole environment: # TZ: 'set timezone' # WEBPASSWORD: 'set a secure password here or it will be random' # Volumes store your data between container upgrades volumes: - './pihole/etc-pihole/:/etc/pihole/' - './pihole/etc-dnsmasq.d/:/etc/dnsmasq.d/' - './pihole/var-log/:/var/log' - './pihole/etc-cont-init.d/10-fixroutes.sh:/etc/cont-init.d/10-fixroutes.sh' # Recommended but not required (DHCP needs NET_ADMIN) # https://github.com/pi-hole/docker-pi-hole#note-on-capabilities cap_add: - NET_ADMIN restart: unless-stopped networks: internal: lan: ipv4_address: 192.168.1.3 networks: internal: lan: name: lan driver: macvlan driver_opts: parent: br-lan.20 ipam: config: - subnet: 192.168.1.0/24
Create the folders for the volumes:
mkdir -p ./pihole/etc-pihole/ mkdir -p ./pihole/etc-dnsmasq.d/ mkdir -p ./pihole/var-log/ mkdir -p ./pihole/var-log/lighttpd chown 33:33 ./pihole/var-log/lighttpd mkdir -p ./pihole/etc-cont-init.d/
echo '#!/usr/bin/with-contenv bash set -e echo "fixing routes" ip route del default ip route add default via 172.18.0.1 echo "done fixing routes"' >> ./pihole/etc-cont-init.d/10-fixroutes.sh chmod 755 ./pihole/etc-cont-init.d/10-fixroutes.sh
cd ~ docker-compose up -d pihole docker logs -f pihole
Wait for PiHole to finish starting up and then hit
CTRL+C. Then run the following:
cd ~/pihole/etc-pihole sed -i -e 's/REV_SERVER.*//; s/REV_SERVER_CIDR.*//; s/REV_SERVER_TARGET.*//; s/REV_SERVER_DOMAIN.*//; s/PIHOLE_INTERFACE.*//' setupVars.conf echo 'REV_SERVER=true REV_SERVER_CIDR=192.168.1.0/24 REV_SERVER_TARGET=127.0.0.11 REV_SERVER_DOMAIN=lan PIHOLE_INTERFACE=eth0' >> setupVars.conf
Restart PiHole container
docker restart pihole
You could do some testing and see if it works. From another machine connected to the LAN network or WiFi:
ping 192.168.1.3 nslookup pihole.lan 192.168.1.3 nslookup openwrt.org 192.168.1.3 # For *nix nslookup $(hostname).lan 192.168.1.3 # For Windows nslookup %COMPUTERNAME%.lan 192.168.1.3
Open PiHole admin: http://pihole.lan/admin
If all of the above works then PiHole container is ready to be the DNS server for your devices on the LAN network.
Look for the following section:
config dhcp 'lan' option interface 'lan' option start '100' option limit '150'
Then add the following line to it:
list dhcp_option '6,192.168.1.3'
dnsmasq service (the service that manages the DHCP on openwrt):
Disconnect and reconnect your devices (or wait for the DHCP leases to expire) and enjoy ad blocking!
This would mean that something is wrong with
macvlan interface or the
docker-compose.yml network related section. Check again these two steps from before.
Check the interfaces in the PiHole container:
docker exec -it pihole ip a
Look for an interface with the IP Address that starts with 172. Is it having the same first three numbers 172.18.0 (the subnet) same as in the
10-fixroutes.sh script? If not then modify
10-fixroutes.sh and fix the subnet to be matching (keeping the last number .1 as is) and then restart the container.
There could be multiple reasons for this, but the basic idea from this test is to ensure that PiHole is forwarding any lookup with
.lan suffix to OpenWrt DNS. OpenWrt creates DNS entries for the devices connecting to the LAN network on the fly with
.lan suffix (as in belonging to the
lan domain). Check again
setupVars.conf, make sure of
REV_SERVER_TARGET=127.0.0.11 in there (notice the extra 1 in the end, that is because 127.0.0.11 is the embedded Docker DNS which eventually forwards to OpenWrt local DNS).
- April 7th 2022: added
dockerd kmod-macvlanto Install Docker and Docker-Compose on OpenWrt. Thanks @grrr2.
- April 10th 2022: minor fixes to the folders creations, the
10-fixroutes.shand changed the PiHole image version to 2021.09 in Setup
docker-compose.ymland PiHole based on this feedback. Thanks @Deuxst.
- April 10th 2022: Added code coloring for bash/shell snips.
- April 13th 2022: Added new section Disable IPv6 DHCP on OpenWrt along with warning above it.
- April 13th 2022: Updated section Test and added command for Windows machines.
- April 13th 2022: Mentioned RPi support.
- April 13th 2022: Added note about PiHole version and issues with latest versions.
- April 22nd 2022: Fixed mistake in Configure DNS settings in OpenWrt and Profit! for the command that restarts the dhcp/dnsmasq service in OpenWrt
- July 25th 2022: Added reboot after installing Docker in Install Docker and Docker-Compose on OpenWrt
- September 28th 2022: Fixed value of
docker-compose.ymland PiHole. Apparently if
hostname: pihole.lan, lookup for
pihole.lanwill actually be blocked, but
hostname: piholewill give successful response for
pihole.lanlookup. This took me a while to figure out...
- September 28th 2022: Updated Tests section to used pihole.lan instead of IP address in URL and pihole.lan lookup.