---
name: pihole
description: Debug and manage a self-hosted Pi-hole v6 Docker deployment — DNS troubleshooting, database queries, whitelisting, and common failure patterns.
category: self-hosting
triggers:
  - "Pi-hole"
  - "pihole"
  - "DNS blocking"
  - "internet not working + Pi-hole"
  - "devices can't connect + DNS"
---

# Pi-hole v6 Debugging & Management

Self-hosted Pi-hole v6 in Docker on a Linux server. Covers diagnosing DNS issues, querying the FTL database, whitelisting, and common false-positive patterns.

See `references/v6-api-quirks.md` for Pi-hole v6 CLI/API changes, `references/query-db.py` for a reusable database query script, and `references/cname-false-positive-evidence.md` for empirical evidence of CNAME false-positive blocking patterns.

## Quick Health Check

```bash
docker ps --filter name=pihole           # is it running?
docker exec pihole pihole status          # FTL + blocking status
dig +short google.com @<pihole-ip>        # does DNS resolution work?
```

## Accessing the FTL Database

Pi-hole v6 stores queries in `/etc/pihole/pihole-FTL.db` inside the container. The host path is under `/var/lib/docker/volumes/pihole_etc/_data/`.

**Permission pitfall:** `/var/lib/docker` has `drwx--x---` permissions — regular users can't traverse it even if the volume files are user-owned. Copy the database out with sudo:

```bash
sudo cp /var/lib/docker/volumes/pihole_etc/_data/pihole-FTL.db /tmp/pihole-FTL.db
sudo cp /var/lib/docker/volumes/pihole_etc/_data/pihole-FTL.db-wal /tmp/pihole-FTL.db-wal
sudo cp /var/lib/docker/volumes/pihole_etc/_data/pihole-FTL.db-shm /tmp/pihole-FTL.db-shm
sudo chown $USER:$USER /tmp/pihole-FTL.db*
```

Then query with Python sqlite3 (the container is Alpine — no python/sqlite3 inside):

```python
import sqlite3
conn = sqlite3.connect('/tmp/pihole-FTL.db')
cur = conn.cursor()
cur.execute("SELECT datetime(timestamp,'unixepoch','localtime'), domain, client, status FROM queries ORDER BY id DESC LIMIT 30")
```

## Pi-hole v6 Status Codes

| Code | Meaning | Notes |
|------|---------|-------|
| 1 | GRAVITY_BLOCKED | Domain in adlist |
| 2 | FORWARDED | Sent to upstream DNS |
| 3 | CACHE | Answered from cache |
| 4 | REGEX_BLOCKED | Matches regex denylist |
| 5 | EXACT_BLOCKED | Exact denylist match |
| 6 | UPSTREAM_BLOCKED | Upstream returned NXDOMAIN/blocked |
| 14 | CACHED_BLOCKED | Previously blocked, cached result — **KEY FALSE-POSITIVE SIGNAL** |
| 17 | RETRIED | First attempt failed, retry succeeded — normal but high % = problem |

**Status 14 is the smoking gun for false positives.** It means a domain was cached as blocked (usually via CNAME chain inspection). Essential domains like `connectivitycheck.gstatic.com` or `dns.msftncsi.com` getting status 14 will break device connectivity checks.

## Deep CNAME Inspection False Positives

**Symptom:** Some devices work, others don't. Android TV, Windows, and IoT devices are most affected — they have strict connectivity checks that fail on first blocked DNS response.

**Root cause:** Pi-hole v6 has `CNAMEdeepInspect = true` by default. When a domain's CNAME chain passes through any blocked domain, the entire chain is blocked. Google infrastructure domains (`gstatic.com`, `l.google.com`, `googlehosted.com`, `1e100.net`) often end up in blocklists as subdomain entries, causing collateral damage.

**Diagnosis:**
```bash
# Check if deep CNAME inspection is on
docker exec pihole cat /etc/pihole/pihole.toml | grep CNAMEdeepInspect

# Find domains getting status 14 (cached false blocks)
python3 -c "
import sqlite3; conn = sqlite3.connect('/tmp/pihole-FTL.db')
cur = conn.cursor()
cur.execute('SELECT domain, count(*) FROM queries WHERE status=14 GROUP BY domain ORDER BY count(*) DESC LIMIT 20')
for d,c in cur.fetchall(): print(f'{c:5}x | {d}')
"
```

**Fix — whitelist the critical domains:**
```bash
docker exec pihole pihole allow connectivitycheck.gstatic.com
docker exec pihole pihole allow dns.msftncsi.com
docker exec pihole pihole allow ota.nvidia.com
docker exec pihole pihole allow clients3.google.com
docker exec pihole pihole allow android.apis.google.com

# Google Cast / Chromecast mtalk domains (alt1 through alt8)
docker exec pihole pihole allow alt1-mtalk.google.com
docker exec pihole pihole allow alt2-mtalk.google.com
docker exec pihole pihole allow alt3-mtalk.google.com
docker exec pihole pihole allow alt4-mtalk.google.com
docker exec pihole pihole allow alt5-mtalk.google.com
docker exec pihole pihole allow alt6-mtalk.google.com
docker exec pihole pihole allow alt7-mtalk.google.com
docker exec pihole pihole allow alt8-mtalk.google.com

# Reload DNS to flush the stale block cache
docker exec pihole pihole reloaddns
```

**Pi-hole v6 CLI change:** `pihole -w` (whitelist) no longer works — use `pihole allow` instead. `pihole -c` (chronometer) is also gone.

## Common Failure Patterns

### "Worked for a minute after restart, then died"
- If DNS queries stop entirely (check DB): device is going to sleep or Ethernet power management is killing the connection. Check device-side power/sleep settings. Android TV: enable Developer Options → Stay Awake.
- If status 14 appears on connectivity domains: CNAME inspection false positive (see above).
- If status 17 rate is >30% for a device: upstream DNS or network issue.

### Some devices have internet, some don't
- Devices bypassing Pi-hole (hardcoded 8.8.8.8) → always work
- Devices using Pi-hole via DHCP → affected by false blocks
- Check which clients are querying Pi-hole vs not: compare ARP table with Pi-hole client list

### ASUS Router DNS Director + Pi-hole = Catastrophic Overblocking

On ASUS routers (RT-AX82U, etc.), the **DNS Director / DNS Filter** feature (under LAN settings) hijacks ALL device DNS queries and redirects them through Pi-hole. Combined with Pi-hole's deep CNAME inspection blocking Google Cast domains, this breaks:

- Chromecast / Google Cast (Android TV, Shield) — `alt1-8-mtalk.google.com` blocked
- Smart home devices, IoT, game consoles — many hardcode 8.8.8.8 but DNS Director captures it
- Any device that expects unfiltered DNS for connectivity checks

**Fix options (pick one):**
1. **Whitelist the domains** (preferred — keeps ad blocking): allow `connectivitycheck.gstatic.com`, `clients3.google.com`, `alt1-mtalk.google.com` through `alt8-mtalk.google.com`, then `pihole reloaddns`.
2. **Disable DNS Director globally**: ASUS LAN → DNS Director → set global rule to "Router" or "No Filtering". Pi-hole stays running; only devices manually pointed to it use it.
3. **Per-device exemption**: DNS Director → set specific devices (Shield IP, etc.) to "No Filtering" while everyone else goes through Pi-hole.

This is a router-level issue — not a Pi-hole bug. DNS Director is the feature that forces all traffic through Pi-hole without the user realizing it.

### "pihole query returns nothing but domain resolves"
- Pi-hole v6 uses a port 4711 telnet API internally. The pihole CLI may need authentication. Direct database queries are more reliable for investigation.

## Router DHCP Setup

The ASUS router at 192.168.50.1 should have Pi-hole set as the ONLY DNS server in DHCP settings. No secondary/fallback DNS — clients will use the fallback to bypass Pi-hole entirely when the primary is slow.
