Giving every homelab device a readable name in Pi-hole
For a while, my Pi-hole client list was a wall of IP addresses. Thirty-some entries, most of them showing raw IPs. Monitoring query logs meant cross-referencing every address against the DHCP lease table to figure out which device was making requests. That’s friction I mostly lived with until I sat down to fix it.
The root cause trips a lot of people up: DNS records and DHCP leases are separate things. Pi-hole resolves client names by looking up the querying IP in its local DNS. If there’s no matching record, it shows the raw IP. A DHCP lease that assigns 10.0.x.x to a Steam Deck doesn’t automatically tell Pi-hole to call that IP steamdeck.internal - that requires an explicit DNS record.
Why the client list matters
The client list is a diagnostic tool. When a device starts making unusual queries - phoning home to something unexpected, a spike in blocked requests - you need to identify it immediately. Having to cross-reference an IP against a DHCP table adds friction to exactly the moment when you want clarity.
Naming conventions
Before adding records, I had to settle on a naming scheme. For most devices it’s straightforward: ps4.internal, mac-mini.internal. The wrinkle is when you have multiple devices of the same type.
I have five Apple TV 4K units spread across different rooms. Numbered names like appletv03.internal require you to remember which number lives where. Room-based names are more useful: appletv.office.internal, appletv.bedroom.internal, appletv.kitchen.internal.
HomePod mini units follow the same pattern. The stereo pair in the office gets homepod01.office.internal and homepod02.office.internal; the bedroom one gets homepod01.bedroom.internal.
For the Mac mini, I have both wired and Wi-Fi interfaces active. Both get records: mini01.internal for the wired interface and mini01-wifi.internal for the wireless one. Without that, one or the other shows as an anonymous IP depending on which interface is active in Pi-hole’s view.
The Ansible playbook
My homelab already maintains a network.yaml state file with every device’s hostname, reserved IP, and MAC address. Rather than managing Pi-hole DNS records as a separate list, I wrote an Ansible playbook that generates them directly from those state files.
The playbook reads all devices from network.yaml and services from services.yaml, builds a deduplicated list of hostname → IP pairs, then applies them to Pi-hole via the v6 API:
- POST to
/api/authto get a session token - GET current
dns.hostsentries from Pi-hole - Diff against desired state - determine what to add, update, or remove
- DELETE stale entries, PUT new ones
- POST to
/api/action/restartdnsto apply - Run Nebula-Sync to replicate to all five replica instances
One encoding gotcha: Pi-hole v6 expects spaces in URL paths encoded as %20. Ansible’s urlencode filter produces + for spaces (query-string convention), which the Pi-hole API rejects silently. The fix is to chain a replace after urlencode:
url: "{{ api_url }}/api/config/dns/hosts/{{ entry | urlencode | replace('+', '%20') }}"
Pi-hole v6 also requires app_sudo to be enabled for API write access. The playbook checks the current setting and enables it automatically if it’s off - I ran into this requirement earlier when setting up Nebula-Sync and built the check in from the start.
What got added
The batch that prompted this included:
- Steam Deck (new to the homelab - got a DHCP lease and DNS record in the same run)
- Five Apple TVs, now named by room
- Multiple HomePods, named by room and position
- Gaming devices: PS4, PS5, Nintendo Switch, gaming PC
- MoCA adapters
- VLAN 30 IoT devices with internet access: Garmin scale and blood pressure monitor, Govee water leak sensor hub, guest iPads
I also cleaned up some stale entries. Dashy had been retired, so its DNS record came out too. The office LG TV was pulled from VLAN 30 internet access and removed from the DNS records at the same time.
Lessons
- Pi-hole doesn’t inherit hostnames from DHCP. The records are managed separately, which means they drift unless you automate the sync.
- Room-based naming beats numbered naming for multi-device setups.
appletv.office.internaltells you what you’re looking at without a cheat sheet. - Not every VLAN 30 device needs a DNS record. If a device has no path to Pi-hole, it never appears in the client list - only the ones with internet access need entries.
- A single state file as the source of truth pays off. Adding a device to
network.yamlonce covers DHCP config, DNS records, and documentation in a single playbook run.
Related reading
Getting Nebula-Sync working with Pi-hole v6: stale passwords and a redirect trap
One of my Pi-hole replicas had zero local DNS records while the master had 78. The trail led through Pi-hole v6 app passwords that did not match my vault, and an HTTP to HTTPS redirect that quietly broke the sync client.
Nebula-Sync, Pi-hole v6 API keys, and the app_sudo teleporter gotcha
After rotating credentials, Nebula-Sync started failing with auth and teleporter errors. The replica needed webserver.api.app_sudo enabled, not just updated API keys in the env file.
Researching MikroTik RouterOS DNS proxy for the homelab
What RouterOS DNS proxy does, how it would touch DHCP, firewall rules, and static DNS, and why I left clients talking to Pi-hole directly.
Ready to Transform Your Career?
Let's work together to unlock your potential and achieve your professional goals.