Almost every smart plug, camera, TV and robot vacuum looks up domain names before it talks to a cloud server. If those lookups go through a DNS server you control, you get a running list of what each device contacts and how often, and you can block domains for selected devices only. Pi-hole is a free DNS server built for exactly this, and it runs well on a Raspberry Pi or any small Linux machine.
This guide sets up Pi-hole v6 in Docker, points the home network at it and uses it to watch and filter IoT traffic. Commands, ports and settings come from the Pi-hole documentation and the official docker-pi-hole repository.
What Pi-hole can and cannot show
Pi-hole sees DNS queries: the domain a device asks for, when, and the answer. It does not see the content of the traffic, and it only sees queries that are actually sent to it. A device with a DNS server hard-coded in its firmware, or one that uses encrypted DNS over HTTPS to its own resolver, never shows up in Pi-hole. Treat the query log as a good picture of most devices, not a complete audit.
Blocking also has a price. A domain that looks like telemetry may be the one a device needs for firmware updates, time sync or its app. Block one domain at a time and check that the device still works.
Requirements
According to the Pi-hole prerequisites page:
- at least 2 GB of free disk space (4 GB recommended) and 512 MB of RAM,
- a static IP address for the Pi-hole host (a DHCP reservation in the router is fine),
- ports 53 TCP/UDP (DNS) and 80/443 TCP (web interface) free on the host.
The Pi-hole binary is pre-built for x86_64, armv6, armv7, armv8 (aarch64) and riscv64, so any current Raspberry Pi works. You also need Docker with the Compose plugin; follow Docker's own installation guide for your system.
Free port 53 on Ubuntu and Fedora
On Ubuntu 17.10+ and Fedora 33+ the systemd-resolved stub resolver occupies port 53 and stops Pi-hole from listening. The Pi-hole Docker documentation gives these three steps:
sudo sh -c 'mkdir -p /etc/systemd/resolved.conf.d && printf "[Resolve]\nDNSStubListener=no\n" | tee /etc/systemd/resolved.conf.d/no-stub.conf'
sudo sh -c 'rm -f /etc/resolv.conf && ln -s /run/systemd/resolve/resolv.conf /etc/resolv.conf'
sudo systemctl restart systemd-resolved
The first disables the stub listener, the second points /etc/resolv.conf at the real upstream servers, and the third applies the change. The documentation names only these two systems; if docker compose up -d reports that port 53 is already in use elsewhere, find and stop the service that holds it.
The compose file
Create a directory, for example ~/pihole, and save this as compose.yaml. It is the official quick-start file with the optional DHCP and NTP lines removed:
services:
pihole:
container_name: pihole
image: pihole/pihole:latest
ports:
- "53:53/tcp"
- "53:53/udp"
- "80:80/tcp"
- "443:443/tcp"
environment:
TZ: 'Europe/Warsaw'
FTLCONF_webserver_api_password: 'change-this-password'
FTLCONF_dns_listeningMode: 'ALL'
volumes:
- './etc-pihole:/etc/pihole'
restart: unless-stopped
Notes on the settings:
TZmakes the logs rotate at local midnight instead of UTC midnight.FTLCONF_webserver_api_passwordsets the web interface password. If you leave it out, Pi-hole generates a random password and prints it in the log; find it withdocker logs piholeand look for "random password". The configuration page also describes aWEBPASSWORD_FILEvariable for Docker secrets if you prefer not to keep the password in the file.FTLCONF_dns_listeningMode: 'ALL'is required with Docker's default bridge network, according to the quick-start file, because queries reach the container from outside its own subnet. The Pi-hole documentation describesALLas "Permit all origins" and warns that the server must be properly firewalled, otherwise it can become an open resolver. In practice: never forward port 53 from the internet to this host.- The volume keeps the configuration and databases when the container is recreated.
- Capabilities: the official file adds
NET_ADMIN(needed only if Pi-hole is your DHCP server),SYS_TIME(only if Pi-hole sets the host clock) and the optionalSYS_NICE. A DNS-only setup needs none of them. The project advises against--privileged.
If port 80 or 443 is already taken on the host, change the mapping on the left side, for example "8080:80/tcp".
Start it:
docker compose up -d
Then open http://<host-IP>/admin/ and log in.
Point the network at Pi-hole
The Pi-hole post-install page lists three options, in order of preference:
- Set Pi-hole as the DNS server in your router's DHCP settings. Every device that gets its address from the router then uses Pi-hole without further changes. Devices pick up the new DNS server when they renew their lease, so restarting them speeds this up. Do not enter a public DNS server as the second server in the same place, or devices may skip Pi-hole whenever they choose to.
- Use Pi-hole's own DHCP server if the router cannot change the DNS setting. Disable DHCP on the router first, and add the
67:67/udpport andNET_ADMINcapability from the official compose file. - Set the DNS server manually on each device, as a last resort.
The documentation also warns about the host itself: if the Pi-hole machine uses Pi-hole for its own DNS and the container stops, the host loses name resolution and may not be able to pull the image to fix it. Keep the host on a different resolver unless you understand that risk.
Give devices names instead of addresses
When the router does DHCP, Pi-hole sees only IP addresses. The Reverse server setting (formerly "conditional forwarding") forwards name lookups for local addresses to the router so that devices appear by name. In Docker it is an environment variable:
FTLCONF_dns_revServers: 'true,192.168.1.0/24,192.168.1.1,lan'
The fields are: enabled, your local address range in CIDR notation, the router's address and the local domain your router uses. Replace the range, address and domain with your own values; the domain depends on the router. A DHCP reservation for each IoT device also helps, because the address in the log then always means the same device.
Read the query log
Open Query Log in the web interface and filter by a client. Things to look for with IoT devices:
- Frequency. A plug that asks for the same domain every few seconds is polling a cloud service.
- Unexpected domains. Analytics, advertising or tracking domains on a device that has no screen.
- Failed and blocked lookups. If a device stops working after you add a blocklist, its recent blocked queries show the cause.
The privacy level setting controls how much Pi-hole stores. Level 0 (the default) shows everything. Higher levels hide domains, then clients, and level 3 disables the query log and long-term logging entirely. For monitoring IoT traffic you need level 0 or 1 at most. Queries stored with hidden details stay hidden even if you lower the level later.
Block domains for IoT devices only
Pi-hole links clients, domains and blocklists through groups. Every client and every entry starts in the special Default group, which cannot be deleted. The group management example in the documentation shows the pattern for per-device rules:
- Create a group, for example "iot".
- Add the IoT devices as clients (by IP address) and assign them to that group.
- Add the domain you want to block to the deny list and assign it to the "iot" group.
- Remove that domain's assignment to the Default group, so that other devices are not affected.
The documentation's example uses raw database statements; the web interface offers the same groups, clients and domain assignments. The exact menu labels can differ between versions, so look for the group management section in the sidebar.
Keep it running
- Updates. The Pi-hole team recommends against automatic container updates (for example with Watchtower). Read the release notes, then pull the new image and recreate the container:
docker compose pullfollowed bydocker compose up -d. Theetc-piholevolume keeps your data. - Blocklists are refreshed automatically once a week by a cron job in the container, early on Sunday morning.
- A backup DNS. If Pi-hole is down, every device on the network loses name resolution. Keep the router's admin page address written down so you can switch DNS back quickly.