I have been looking to free up one of my rPi's from PiHole duty for a while now, and I was pleasantly surprised to see it is indeed possible to run a containerized instance of PiHole on a UDM, thanks to the excellent work of boostchicken, who created several utility scripts to expedite the process. This quick gist documents my setup steps as of January 2023, and also just generally improves the formatting of the order of operations. You probably want to start here.
Step zero for me was figuring out SSH access for UniFiOS. This (to my knowledge) isn't able to be enabled from UniFiOS at https://192.168.1.1—you'll need to visit the UniFi Portal at unifi.ui.com, navigate to your network, then Settings → System → Network Device Authentication and enable Device SSH Authentication. Enabling SSH in your local console controls won't cut it.
It's also worth noting UniFiOS as of 1.12.33 depends on the insecure ssh-rsa
key negotiation protocol, so ~/.ssh/config
should be amended like so:
ssh udm
PubkeyAcceptedAlgorithms +ssh-rsa
HostkeyAlgorithms +ssh-rsa
-
SSH in, and grab a copy of the boot script:
curl -fsL "https://raw.githubusercontent.com/unifi-utilities/unifios-utilities/HEAD/on-boot-script/remote_install.sh" | /bin/sh
This will install CNI plugins for the ARM64 architecture onto your UDM—this is necessary as we'll be running PiHole via
podman
. Once complete, you'll have a directory at/mnt/data/on_boot.d
to place files which will be executed on boot of your UDM. This ensures pihole is both restarted on-boot, and even across firmware updates. -
Copy the
05-install-cni-plugins.sh
file to this new directory, and execute it. If you've run the remote installer, you'll already have this file in your directory, and can skip right to executing it.cd /mnt/data/on_boot.d curl -J https://raw.githubusercontent.com/unifi-utilities/unifios-utilities/main/cni-plugins/05-install-cni-plugins.sh chmod +x 05-install-cni-plugins.sh /mnt/data/on_boot.d/05-install-cni-plugins.sh
-
Create a new VLAN in your UDM here. I didn't think the example configuration was that intuitive so I chose my own settings:
Setting Value Network Name PiHole Auto-Scale Network false Gateway IP/Subnet 192.168.2.1/26 VLAN ID 2 Network Type Standard Content Filtering None Multicast DNS true DHCP Mode None 192.168.1.x
is my primary network, so incrementing the third component of the IP seemed like a sensible choice, and I don't need the space a/24
subnet provides—and again, a VLAN ID of 2, aligning it with the Gateway IP, seemed reasonable.Very important: DHCP mode must be set to
None
. -
Amend
20-dns.conflist
, to match your settings:- Replace
name
with the name of your network above. - For
plugins.0.mac
, ensure thexx:xx:xx
component of the MAC address is replaced, i.e.00:00:00
. There is no real reason for it to be random or otherwise randomly generated, and setting it explicitly to such a value makes it easy to identify (however, the choice to use Iridium's MAC namespace here is somewhat questionable in my opinion, though). - Replace
plugins.0.master
with the identifier of your VLAN you've created, prefixed with "br". In my case,"br2"
. - Replace
plugins.0.ipam.addresses.0.address
with the CIDR-notated address of the address of your PiHole instance. This differs from the gateway address of your VLAN. Given that I chose a gateway address of192.168.2.1
, using192.168.2.2
for my PiHole instance seemed sensible—this will also become your DNS resolver once operational.
Following these changes, my
20-dns.conflist
was as such:{ "cniVersion": "0.4.0", "name": "PiHole", "plugins": [ { "type": "macvlan", "mode": "bridge", "master": "br2", "mac": "00:1c:b4:00:00:00", "ipam": { "type": "static", "addresses": [ { "address": "192.168.2.2/26", "gateway": "192.168.2.1" } ], "routes": [{ "dst": "0.0.0.0/0" }] } } ] }
This step, represented in bash:
cd /mnt/data/podman/cni curl -J https://raw.githubusercontent.com/unifi-utilities/unifios-utilities/main/cni-plugins/20-dns.conflist nano 20-dns.conflist # amend file here as described above. chmod +x 20-dns.conflist cp 20-dns.conflist /etc/cni/net.d/dns.conflist
Finally, if everything has worked as expected, when you run
podman network inspect PiHole
, you'll see your configuration you entered above. - Replace
-
Modify the arguments within
10-dns.sh
to match your configuration, and execute it. Amend the arguments as so:- Modify
VLAN
to match the identifier of your VLAN (for me,2
). - Modify
IPV4_IP
to match the IP address of your PiHole instance (192.168.2.2
). - Modify
IPV4_GW
to match the CIDR-notated IP of the network (192.168.2.1/26
). - Modify
CONTAINER
to match the name of the docker container you're about to run.
cd /mnt/data/on_boot.d curl -J https://raw.githubusercontent.com/unifi-utilities/unifios-utilities/main/dns-common/on_boot.d/10-dns.sh nano 10-dns.sh # amend file here as described above. chmod +x 10-dns.sh /mnt/data/on_boot.d/10-dns.sh
You may see this error appear on execution, this is normal and fine.
- Modify
-
Create the following directories. These will be mounted as persistent volumes for our pihole container.
mkdir -p /mnt/data/etc-pihole mkdir -p /mnt/data/pihole/etc-dnsmasq.d
-
Finally, time to create our pihole container instance. The following entries must match:
- As mentioned above,
--name
should equal the container name defined in10-dns.sh
. --network
should equal the name you defined in20-dns.conflist
.-e FTLCONF_REPLY_ADDR4
must match the IP address you're defining for your PiHole instance.
Some other notes:
--cap-add=NET_ADMIN
is necesary to run the DHCP server.-e TZ
should match the IANA tz identifier that your UniFiOS console is running.- I decided to add
-e FTLCONF_BLOCK_ICLOUD_PR="false"
to support iCloud Private Relay on my home network. - I also chose to include
-e FTLCONF_PRIVACY_LEVEL="0"
to allow for more detailed PiHole usage statistics. - All FTL configuration settings can be set in this fashion when launching a container instance of pihole.
- I exclusively decided to use 1.1.1.1 as my DNS resolver.
podman run -d \ --network PiHole \ --restart always \ --name pihole \ --cap-add=NET_ADMIN \ -v "/mnt/data/etc-pihole/:/etc/pihole/" \ -v "/mnt/data/pihole/etc-dnsmasq.d/:/etc/dnsmasq.d/" \ --dns=127.0.0.1 \ --dns=1.1.1.1 \ --hostname pi.hole \ -e TZ="Pacific/Auckland" \ -e VIRTUAL_HOST="pi.hole" \ -e PROXY_LOCATION="pi.hole" \ -e FTLCONF_REPLY_ADDR4="192.168.2.2" \ -e FTLCONF_BLOCK_ICLOUD_PR="false" \ -e FTLCONF_PRIVACY_LEVEL="0" \ -e IPv6="False" \ pihole/pihole:latest
Check your container works with
podman container ls
. - As mentioned above,
-
Provision your PiHole instance by setting a password:
podman exec -it pihole pihole -a -p PW
. -
Configure your WAN such that it's primary DNS Server is set to the IP address of your PiHole instance, in my case
192.168.2.2
. -
Likewise configure your LAN's DHCP Server to be enabled, and pointing to the same IP address.
-
Enjoy the PiHole!
Superseded by PiHole on UniFi OS 3.