Skip to content

Pi-hole

This content is not available in your language yet.

The DNS ad blocker: whether it is blocking, how many queries it answers and blocks, how old its blocklists are, whether its upstream resolvers fail, whether an update waits, and the messages of its own diagnosis page.

From the DumbMonit server (or its relay agent) to the device:

PortProtocolPurpose
80TCPAPI (HTTP, default)
443TCPOnly with the https scheme option

DumbMonit reads Pi-hole v6’s REST API (/api). Pi-hole 5 and earlier have no such API and are not supported.

What goes wrong with a Pi-hole rarely shows: DNS keeps answering. Blocking is paused “for five minutes” and never comes back, the weekly blocklist update (gravity) has failed for a month, an upstream resolver answers SERVFAIL, and the diagnosis page nobody opens has been listing the reason all along.

All metrics are prefixed dumbmonit_pihole_. Pi-hole counts queries over the last 24 hours, a sliding window that goes up and down: those figures are gauges, suffixed _24h, not counters.

MetricWhatLabels
blocking_enabled1 when blocking is on; 0 when it is off, paused or failed
blocking_timer_secondstime left before a paused blocking turns itself back on
queries_24h, queries_blocked_24h, queries_blocked_percentqueries answered and blocked in the last 24 hours
queries_forwarded_24h, queries_cached_24h, unique_domains_24hqueries sent upstream, answered from the cache, distinct domains
queries_per_secondaverage over the last minute
replies_servfail_24hSERVFAIL answers in the last 24 hours: an upstream resolver that fails shows here first
clients_active, clients_totalclients seen
gravity_domainsdomains on the blocklists
gravity_last_update_timestamp_seconds, gravity_age_secondslast blocklist rebuild, and how long ago
version_infovalue 1core, web, ftl, docker
update_available1 when a newer version of a component is published, 0 otherwise; absent until Pi-hole’s daily check has runcomponent (core, web, ftl, docker)
updates_availableupdates to install: the image in Docker (core, web and FTL come with it), otherwise each outdated component
messages, messages_by_typemessages on the diagnosis pagetype (LOAD, RATE_LIMIT, LIST…)
upstream_queries_24h, upstream_response_secondsqueries sent to each resolver and its average answer time (at most 8; the virtual blocklist and cache entries are left out, and a resolver that was not asked has no time)upstream (ip:port)
lists_enabled, lists_download_failed, lists_invalid_domainsenabled lists, those whose last gravity download failed (unreachable, with or without a local copy), invalid entries found; lists_download_failed is absent from Pi-hole builds that do not report list status
dhcp_leasesactive leases of Pi-hole’s DHCP server; absent while it has none
ftl_uptime_seconds, ftl_memory_percent, ftl_cpu_percentthe FTL daemon
privacy_levelPi-hole’s privacy level, 0 to 3
scrape_errorssecondary API calls that failed during the last check

On a development branch, Pi-hole compares commits rather than version numbers, and so does DumbMonit.

The built-in rules that apply: Pi-hole blocking disabled, Pi-hole blocklists stale, Pi-hole update available, Pi-hole diagnosis messages, Pi-hole SERVFAIL replies, Pi-hole upstream resolver slow, Pi-hole blocklist download failing, Pi-hole receives no queries, plus Monitor unreachable.

The three extra calls (/api/stats/upstreams, /api/lists, /api/dhcp/leases) are plain reads. The DHCP one is optional: when Pi-hole refuses it or has no DHCP server, nothing is reported and no error is counted.

The panel above the charts reads what the check stored; opening the page never connects to Pi-hole. It shows whether blocking is on (and when a pause ends), the queries and share blocked in the last 24 hours, the blocklist size and age, the clients, the versions with any update waiting, and the number of diagnosis messages.

  1. In Pi-hole’s web interface, open Settings → Web interface / API and switch the page from Basic to Expert.

  2. Click Configure app password, copy the password shown, then click Enable new app password.

  3. Check that Settings → All settings → Webserver and API → webserver.api.app_sudo is off, its default.

  4. In DumbMonit, enter Pi-hole’s address and paste the app password, or pick No password if the web interface has none.

Vendor documentation: https://docs.pi-hole.net/api/

Pi-hole v6 or later is required: version 5 had no REST API. Pi-hole has no read-only account; the closest is an app password, which cannot change any setting as long as webserver.api.app_sudo is false, the default. Pi-hole shows the app password only once, and a new one replaces the previous one. You can also check the setting on the Pi-hole machine (in Docker, prefix the command with docker exec and the container name):

pihole-FTL --config webserver.api.app_sudo

DumbMonit only reads. It never pauses blocking and never changes a list, and it keeps one API session open between checks instead of opening one per check: Pi-hole allows 16 sessions at a time. Pi-hole serves plain HTTP on port 80, and the password crosses the network each time a session opens: across an untrusted network, use HTTPS on port 443.

An app password also logs in without the second factor when two-factor authentication is on: that is what it is for. To revoke it, empty webserver.api.app_pwhash under Settings → All settings, or enable a new one: the previous one stops working at once.

CredentialFields
App passwordThe password generated above. DumbMonit exchanges it for a session, sent in the X-FTL-SID header, and reuses that session while Pi-hole keeps it (30 minutes after its last use).
No passwordOnly for a Pi-hole whose web interface has no password.

Address: a host name or IP (pi.hole), host:port, or a full URL (https://pi.hole); the path of the web interface or of the API, copied from the browser, is ignored. Options: protocol (HTTP by default), port (80), certificate check (for HTTPS on 443, Pi-hole’s own certificate is self-signed) and request timeout (10 s).

If a blocklist rule fires, open Pi-hole’s Adlists page: the list’s status shows the download error, and pihole -g rebuilds gravity by hand. A high SERVFAIL share usually means the upstream resolver (Unbound, a router, a public resolver) is unreachable from the Pi-hole machine.

If checks fail with “no free API session”, Pi-hole’s sessions are all taken, usually by a script that logs in on every run without logging out. They expire after 30 minutes; webserver.api.max_sessions raises the limit.