AdGuard Home
AdGuard Home is read the same way as Pi-hole: the query log with the filter verdict becomes DNS events, the persistent and discovered clients become device names, and the hardware addresses AdGuard lists become identity anchors. It is a Free connector.
This page covers what differs from Pi-hole. Everything about what the connector
emits, how the naming job ranks it and where it shows up is on the
Pi-hole page and applies here with
adguard in place of pihole.
What it reads
Section titled “What it reads”AdGuard Home v0.107 or later, over its REST API:
| Endpoint | Purpose |
|---|---|
/control/status |
Version and state, for the test connection |
/control/querylog |
The query log, walked newest first with older_than |
/control/stats |
AdGuard’s own counters for the statistics window |
/control/clients |
Persistent clients (ids with address and MAC) and auto-discovered clients |
/control/dhcp/status |
Leases, when AdGuard is the DHCP server |
A query is blocked when AdGuard’s reason starts with Filtered; the reason
is kept as recorded (FilteredBlackList: ||rule^). A client discovered through
DHCP, or a lease AdGuard serves, gives a dhcp name; a persistent client and a
client discovered through rDNS, the hosts file or ARP gives a resolver name.
AdGuard on a platform without DHCP answers 501 on the DHCP endpoint, which the
connector treats as not served, never as an error.
Set it up
Section titled “Set it up”1. Create a dedicated user
Section titled “1. Create a dedicated user”AdGuard Home authenticates with HTTP basic authentication and has no read-only
role. Create a separate user — Settings → General settings, or users: in
AdGuardHome.yaml — so the credential can be revoked on its own, and store
both halves as credential files under the rules of
UniFi, step 3:
sudo install -m 0640 -o root -g nyxtrace /dev/null /etc/ndr/secrets/adguard-usernamesudo install -m 0640 -o root -g nyxtrace /dev/null /etc/ndr/secrets/adguard-passwordsudo sh -c 'printf %s "<username>" > /etc/ndr/secrets/adguard-username'sudo sh -c 'read -rs p && printf %s "$p" > /etc/ndr/secrets/adguard-password'2. Declare the instance
Section titled “2. Declare the instance”connectors: - id: adguard instance: adguard tenant: example enabled: true config: url: http://adguard.example.invalid:3000 # tls_fingerprint_sha256: "<sha256 of the leaf>" only for https with a self-signed certificate poll_interval: 60s # floor 30s page_size: 500 # 10..500 (AdGuard's own cap), twenty requests per poll secrets: username: file:/etc/ndr/secrets/adguard-username password: file:/etc/ndr/secrets/adguard-passwordNDR_CONNECTORS_ENABLED=true in /etc/ndr/ndr.env is required as for every
connector declared in the file — see
connectors.yaml. The same schema migrations,
DROP VIEW grant and one-time backfill as for Pi-hole apply: see
Pi-hole, step 3
and migrations.
3. Prove it before the service polls it
Section titled “3. Prove it before the service polls it”sudo -u nyxtrace env NDR_CONNECTORS_ENABLED=true ndr connectors validatesudo -u nyxtrace env NDR_CONNECTORS_ENABLED=true ndr connectors test adguard --livesudo -u nyxtrace env NDR_CONNECTORS_ENABLED=true ndr connectors collect adguard --once --livetest --live reports the AdGuard version and the queries and blocked count of
AdGuard’s statistics window. The failure classes are the ones on the
Pi-hole page; a
401 is auth.
4. Switch on and verify
Section titled “4. Switch on and verify”NDR_CONNECTORS_ENABLED=true and systemctl restart ndr-api. The
system view shows connector:adguard with the counters
queries and blocked (delivered by the last poll), clients, and AdGuard’s
own queries_window and blocked_window. Stale after three missed polls;
degraded, with the reason on the health line, when a poll refused rows.
journalctl -u ndr-api -g 'instance=adguard'Starting an instance over
Section titled “Starting an instance over”The same stop → reset → start sequence as for Pi-hole, with this instance’s name:
systemctl stop ndr-apisystemd-run --pipe --quiet --collect --uid=nyxtrace \ -p EnvironmentFile=/etc/ndr/ndr.env -p EnvironmentFile=/etc/ndr/netflow.env \ /usr/local/bin/ndr connectors reset-cursor adguardsystemctl start ndr-apiStop first: a running ndr-api holds the old bookmark in memory and would
write it back after its next poll. The next poll then takes the newest page of
the log, as a first poll does, and already delivered queries are absorbed by
the event table’s replacing key.
Bounds
Section titled “Bounds”- Poll 60 s, floor 30 s; one request 5 s, one collection 1 min, one answer 4 MiB.
page_size10..500 — AdGuard’s own cap — and at most twenty requests per poll, so 10,000 queries per poll at the default. A poll that needs more istruncated, and because the log is walked newest first the queries that fall off are the oldest of the window.- The first poll takes exactly the newest page of the log, not a time window: a window would depend on the appliance clock agreeing with the resolver’s. From then on the log is walked back to the cursor.
- Queries from a client outside the tenant’s own networks are dropped and
counted in
dropped=. - A query stamped more than five minutes ahead of the appliance clock is
refused and counted as failed, and the instance is
degradedwith the reasonquery time is more than 5m ahead of the appliance clockuntil a poll refuses nothing; the cursor never moves past the present, and a bookmark an earlier release let run into the future is rewound once. The comparison is in absolute time, so a resolver in another time zone is not skew. Fix the clock (NTP) and the state clears — the rule is described under Pi-hole.
Known limits
Section titled “Known limits”resolver_blockedis shown and stored, but no detection model reads it yet.- Two AdGuard instances are two entries with different
instancenames; each keeps its own cursor, backoff and health line.