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).
Note: The PiHole Image version being used in this tutorial is 2021.09. Recent images seem to be having an issue with permissions as shown here and here.
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)
Disable IPv6 DHCP on OpenWrt
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:
Install Docker and Docker-Compose on OpenWrt
opkg update && opkg install docker luci-app-dockerman docker-compose dockerd kmod-macvlan
reboot
Setup MACVLAN interface in OpenWrt
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.
vi /etc/config/network
Look for the config interface 'lan'
section and note down the device from the option device
line. In my case it is br-lan
.
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 .20
suffix.
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'
Modify the firewall adding the new interface to the lan
zone
vi /etc/config/firewall
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'
Restart the network and firewall services
/etc/init.d/network restart
/etc/init.d/firewall restart
Setup docker-compose.yml
and PiHole
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/
Create 10-fixroutes.sh
.
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
Start PiHole and finalize its setup
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
Test
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.
Configure DNS settings in OpenWrt and Profit!
vi /etc/config/dhcp
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'
Restart dnsmasq
service (the service that manages the DHCP on openwrt):
/etc/init.d/dnsmasq restart
Disconnect and reconnect your devices (or wait for the DHCP leases to expire) and enjoy ad blocking!
Troubleshooting
Pinging PiHole IP is not working or opening the PiHole admin is not working
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.
nslookup openwrt.org 192.168.1.3
is not working
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.
nslookup $(hostname).lan 192.168.1.3
or nslookup %COMPUTERNAME%.lan 192.168.1.3
is not working
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).
Change Notes:
- April 7th 2022: added
dockerd kmod-macvlan
to Install Docker and Docker-Compose on OpenWrt. Thanks @grrr2. - April 10th 2022: minor fixes to the folders creations, the
10-fixroutes.sh
and changed the PiHole image version to 2021.09 in Setupdocker-compose.yml
and 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
hostname
in Setupdocker-compose.yml
and PiHole. Apparently ifhostname: pihole.lan
, lookup forpihole
orpihole.lan
will actually be blocked, buthostname: pihole
will give successful response forpihole
orpihole.lan
lookup. 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.