๐ก๏ธ Pi-hole Exporter
๐ฏ Purpose
pihole_exporter exposes Pi-hole DNS and ad-blocking metrics for Prometheus and Grafana. In this environment, it provides:
- Query volume, block rate, and cache usage for each Pi-hole DNS server
- Blocking status and blocklist (gravity) size
- Top queried domains, top blocked domains, and top clients
- Upstream resolver usage and response times for the local Unbound resolver
- A DNS view alongside the host-level metrics from Node Exporter on the same servers
โ๏ธ How It Works
pihole_exporter(eko/pihole-exporter) runs on each Pi-hole host (dns-0anddns-1) and listens on port9617- Each exporter reads its local Pi-hole v6 API at
http://127.0.0.1:80, so each host reports its own counters - The exporter logs in with the Pi-hole password, reuses the API session, and renews it only when Pi-hole rejects it
- Prometheus scrapes
/metricsevery 30 seconds with a 15 second timeout - Grafana uses the resulting metrics for Pi-hole dashboards
๐งฉ Key Concepts
๐ Summary Metrics
These gauges cover the rolling last 24 hours, the same numbers shown on the Pi-hole web dashboard:
pihole_statusโ1when blocking is enabled,0when disabledpihole_domains_being_blockedโ number of domains in the gravity blocklistpihole_dns_queries_todayandpihole_dns_queries_all_typesโ total DNS queriespihole_ads_blocked_todayandpihole_ads_percentage_todayโ blocked queries, as a count and a percentagepihole_queries_forwardedandpihole_queries_cachedโ queries answered upstream or from cachepihole_unique_domainsโ distinct domains queriedpihole_unique_clientsandpihole_clients_ever_seenโ client countspihole_request_rateโ current requests per second
๐ Breakdown Metrics
pihole_querytypesโ queries by record type (typelabel: A, AAAA, HTTPS, PTR, and so on)pihole_replyโ replies by type (typelabel: NODATA, NXDOMAIN, CNAME, IP, and so on)pihole_top_queriesandpihole_top_adsโ most queried and most blocked domains (domainlabel)pihole_top_sourcesandpihole_top_sources_blockedโ busiest clients and clients with the most blocked queries (sourceandsource_namelabels)pihole_forward_destinationsโ queries per upstream (destinationanddestination_namelabels)pihole_forward_destinations_responsetimeandpihole_forward_destinations_responsevarianceโ upstream response time
Pi-hole forwards to the local Unbound resolver (127.0.0.1#5335), so that is the main upstream destination. The forward destination metrics also include Pi-holeโs own cache and blocklist entries.
๐ท๏ธ Labels
Every metric carries a hostname label with the address the exporter used to reach Pi-hole. Because each exporter reads its local API, this is 127.0.0.1 on both hosts. Use the Prometheus instance label (dns-0 or dns-1) to tell the servers apart.
The top lists are snapshots. Entries that drop out of a top list stop being exported, so these series come and go over time.
๐ Configuration & Service
Binary:
/usr/local/bin/pihole_exporter, a link to the versioned binary in /opt/pihole_exporter
Password file:
/etc/pihole_exporter/password (root:pihole-exporter, mode 0640)
Service:
pihole_exporter (systemd)
Listener:
0.0.0.0:9617
The service runs as the pihole-exporter system user. All settings are passed as command-line flags in the systemd unit. The password is read from the password file through -pihole_password_file, so it never appears in the unit file or the process list.
Role settings (roles/pihole_exporter_setup/defaults/main/main.yml):
pihole_exporter_setup_version: "1.3.0"pihole_exporter_setup_port: 9617pihole_exporter_setup_pihole_hostname: "127.0.0.1"pihole_exporter_setup_pihole_port: 80pihole_exporter_setup_passwordโ defaults to thepiholeroleโs vaultedpihole_password
Prometheus settings (roles/prometheus_setup/defaults/main/main.yml):
prometheus_setup_pihole_exporter_job_name: "pihole_exporter"prometheus_setup_pihole_exporter_port: 9617prometheus_setup_pihole_alerts_enabled: true
The password is supplied through Ansible vault; do not put it in this page, metric labels, or other public configuration.
๐ Deployment
The pihole_exporter inventory group in inventory/dns/inventory.ini contains the Pi-hole hosts. Deploy the exporter with:
source /opt/python_3.12/bin/activate
ansible-playbook -i inventory/dns/inventory.ini playbooks/prometheus/deploy_pihole_exporter.yml
The playbook loads roles/pihole/defaults/main/vault.yml so the exporter uses the same vaulted pihole_password as the pihole role. To use a Pi-hole application password instead (Settings โ Web interface / API), set pihole_exporter_setup_password in vault.
Refresh Prometheus scrape targets after inventory changes:
ansible-playbook playbooks/prometheus/deploy_prometheus_exporters.yml
This refresh command uses the repositoryโs full combined inventory (inventory/); do not limit it to inventory/dns/, or exporter groups from other inventories will be omitted from that run.
Deploy the Pi-hole alert rules with the main Prometheus playbook, then refresh the exporter targets again:
ansible-playbook -i inventory/prometheus/inventory.ini playbooks/prometheus/deploy_prometheus.yml
ansible-playbook playbooks/prometheus/deploy_prometheus_exporters.yml
Syntax-check the deployment playbook before applying changes:
ansible-playbook -i inventory/dns/inventory.ini --syntax-check playbooks/prometheus/deploy_pihole_exporter.yml
๐ Alert Rules
The pihole group in prometheus.rules.yml contains:
PiholeExporterDownโ the exporter cannot be scraped for 5 minutes (critical)PiholeMetricsAbsentโ the exporter is up but returns no Pi-hole data for 10 minutes (warning)PiholeBlockingDisabledโ blocking is disabled for 15 minutes (warning)PiholeGravityEmptyโ the blocklist has no domains for 15 minutes (warning)
prometheus.yml does not currently have an alerting: section, so these alerts appear in Prometheus and Grafana but are not sent to Alertmanager.
๐ Common Prometheus Queries
- Check that each exporter endpoint is reachable:
up{job="pihole_exporter"} - Check blocking status on each server:
pihole_status - Compare query load between the two servers:
pihole_dns_queries_today - Check the block rate:
pihole_ads_percentage_today - Check the cache hit ratio:
pihole_queries_cached / (pihole_queries_cached + pihole_queries_forwarded) - Find the busiest clients:
topk(10, max by (source, source_name) (pihole_top_sources)) - Find the most blocked domains:
topk(10, max by (domain) (pihole_top_ads)) - Check upstream response time:
pihole_forward_destinations_responsetime
๐ Grafana Usage Notes
No Pi-hole dashboard is provisioned yet. The exporter project publishes one (grafana.com ID 10176) that works with these metrics.
When building panels, group by instance rather than hostname, because hostname is 127.0.0.1 on both servers. Clients usually query both Pi-hole servers, so add the two serversโ counts together for a network-wide total.
๐ ๏ธ Troubleshooting
โ Exporter Target Is Down
Check:
- The
pihole_exportersystemd service is active on the DNS host - Port
9617is reachable from Prometheus - The target address matches the hostโs
global_ip_addressesentry
๐ญ Target Is Up but There Is No Pi-hole Data
Check:
pihole-FTLis running and the web API answers on port80- The password in
/etc/pihole_exporter/passwordis correct for this host - The service logs for
401or connection errors
This is the condition that triggers PiholeMetricsAbsent. If only one host is affected, its web password may differ from the other hostโs.
๐ซ Blocking Disabled or Empty Blocklist
Check:
pihole statuson the host- Re-enable blocking with
pihole enableor in the web interface - Rebuild the blocklist with
pihole -gifpihole_domains_being_blockedis0
๐ ๏ธ Quick Verification Commands
systemctl status pihole_exporter
journalctl -u pihole_exporter -n 50 --no-pager
curl -s http://localhost:9617/metrics | grep -E '^pihole_(status|domains_being_blocked)'
/usr/local/bin/pihole_exporter -version
๐ Security & Operational Guidance
- Keep the Pi-hole password in Ansible vault; never put it in source-controlled plaintext
- Prefer a Pi-hole application password over the web interface password
- Restrict access to port
9617to Prometheus and trusted administration hosts; the top-client and top-domain metrics reveal browsing activity on the network - Protect
/etc/pihole_exporter/password; it is readable only by root and the exporterโs group - Each exporter keeps one API session open; it closes the session when the service stops
๐งพ Summary
pihole_exporter exposes Pi-hole query, blocking, client, and upstream metrics from each DNS server for Prometheus to scrape. Its alert rules flag a stopped exporter, disabled blocking, or an empty blocklist, while Node Exporter separately reports host-level health for the same servers.