# Pi-hole v6 Diagnostics

Quick-start for diagnosing "is Pi-hole causing my network issues?" questions. Pi-hole v6 changed the API, CLI, and database schema — the old `pihole -c` (chronometer), `/admin/api.php`, and v5 commands don't work.

## Decision Flow

1. **Check if Pi-hole is even the problem** before digging in. If DNS resolves through Pi-hole and no essential domains are blocked, the issue is at the WiFi/router/IP level — not Pi-hole.
2. If Pi-hole might be the culprit, query its SQLite database directly.

## Docker Status

```bash
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" | grep pihole
```

Pi-hole v6 container info:
- Status should show `healthy`
- Binds port 53 TCP+UDP to host IP (typically `192.168.50.X:53`)
- Web UI on port 80 inside container, mapped to host (e.g., `8090:80`)

## Pi-hole Process Check

```bash
docker exec pihole pihole status
```

Expected output:
```
[✓] FTL is listening on port 53
    [✓] UDP (IPv4)
    [✓] TCP (IPv4)
    [✓] UDP (IPv6)
    [✓] TCP (IPv6)
[✓] Pi-hole blocking is enabled
```

## Querying the Database (When Web UI Is Locked)

Pi-hole v6's web API at `/api` requires authentication (session or app password). When locked out, query the SQLite database directly.

### Database Location

- **Inside container:** `/etc/pihole/pihole-FTL.db`
- **On host (Docker volume):** `/var/lib/docker/volumes/pihole_etc/_data/pihole-FTL.db`

### Access Pattern

The `/var/lib/docker` directory has `drwx--x---` permissions — regular users can't traverse it. Copy the DB to a readable location:

```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 (the Pi-hole container has no Python/sqlite3):

```python
import sqlite3
conn = sqlite3.connect('/tmp/pihole-FTL.db')
cur = conn.cursor()

# Recent queries
cur.execute("""
    SELECT datetime(timestamp, 'unixepoch', 'localtime'), domain, client, status
    FROM queries ORDER BY id DESC LIMIT 30
""")
```

## Pi-hole v6 Status Codes

Status codes in the `queries` table:

| Code | Meaning | Notes |
|------|---------|-------|
| 1 | GRAVITY_BLOCKED | Domain in blocklist |
| 2 | FORWARDED | Sent to upstream DNS |
| 3 | CACHE | Answered from cache |
| 4 | REGEX_BLOCKED | Matched regex block rule |
| 5 | EXACT_BLOCKED | Exact-match block |
| 6 | UPSTREAM_BLOCKED | Blocked by upstream DNS |
| 9 | CACHE_STALE | Stale cache entry |
| 12 | ALREADY_FORWARDED | Duplicate suppressed |
| 14 | CACHED_BLOCKED | Cached as blocked (from prior CNAME-chain inspection) — **this is the dangerous one for false positives** |
| 16 | ALREADY_BLOCKED (variant) | Gravity already known |
| 17 | RETRIED | First attempt failed, retry succeeded |

**Status 17 is normal** — it's the dominant status in v6 and means the query succeeded on retry. It does NOT indicate a problem.

**Only status 1, 4, 5, 6 are actual blocks.** Everything else = allowed.

## Key Diagnostic Queries

### Blocked vs Allowed Ratio (last hour)
```sql
SELECT CASE WHEN status IN (1,4,5,6) THEN 'BLOCKED' ELSE 'ALLOWED' END, count(*)
FROM queries WHERE timestamp > strftime('%s','now','-1 hour')
GROUP BY 1
```

### Which Clients Are Using Pi-hole
```sql
SELECT client, count(*) FROM queries
WHERE timestamp > strftime('%s','now','-1 hour')
GROUP BY client ORDER BY count(*) DESC
```

### Recent Blocked Domains
```sql
SELECT domain, count(*) FROM queries
WHERE timestamp > strftime('%s','now','-1 hour') AND status IN (1,4,5)
GROUP BY domain ORDER BY count(*) DESC LIMIT 10
```

### Windows NCSI Pattern (Connectivity Check)
Windows devices query `dns.msftncsi.com` every ~60 seconds to verify internet connectivity. If this shows status 17 (RETRIED), the device may be reporting "no internet" even though DNS eventually resolves. This is typically an upstream DNS slowness issue, not a Pi-hole problem.

## 🔥 Primary Pitfall: Deep CNAME Inspection False Positives

**This is the #1 cause of "some devices have no internet after DNS change to Pi-hole."**

When `CNAMEdeepInspect = true` (default in v6), Pi-hole follows CNAME chains. If ANY domain in the chain matches a blocklist entry, the ENTIRE chain is cached as blocked (status 14). The blocklist (e.g., StevenBlack) includes subdomains like `pagead.l.google.com` — these are ad-specific, but CNAME inspection propagates the block to the PARENT `l.google.com`, which kills ALL services on Google infrastructure.

**Affected domains (false positives):**
- `connectivitycheck.gstatic.com` — Android TV/Shield connectivity check
- `dns.msftncsi.com` — Windows NCSI connectivity check
- `google.com` — basic connectivity
- `ota.nvidia.com` — Shield TV updates
- `clients3.google.com`, `android.apis.google.com` — Google Play Services

**Detection:** Look for status 14 on these domains:
```sql
SELECT domain, count(*) FROM queries WHERE status=14
GROUP BY domain ORDER BY count(*) DESC LIMIT 20;
```

**Fix — whitelist critical domains (preferred):**
```bash
docker exec pihole pihole -w connectivitycheck.gstatic.com
docker exec pihole pihole -w dns.msftncsi.com
docker exec pihole pihole -w clients3.google.com
docker exec pihole pihole -w ota.nvidia.com
```

**Alternative — disable deep CNAME inspection:**
Set `CNAMEdeepInspect = false` in `/etc/pihole/pihole.toml` (may allow some CNAME-cloaked ads through).

See also: `pihole-troubleshooting` skill for full diagnostic workflow.

## Red Flags (Pi-hole IS the Problem)

- Essential domains (google.com, microsoft.com, apple.com) appearing with status 1, 4, or 5
- Status 14 on connectivity-check domains (connectivitycheck.gstatic.com, dns.msftncsi.com) — indicates deep CNAME inspection false positive
- Large block percentage (>50%) in last hour
- DNS resolution fails from other hosts: `dig @192.168.50.X google.com` times out
- Pi-hole container unhealthy or in restart loop

## Red Herrings (Pi-hole is NOT the Problem)

- Tracking/analytics domains blocked (app-measurement.com, newrelic.com, doubleclick.net) — these are working as intended
- Status 17 (RETRIED) on queries — normal v6 behavior, queries succeed
- Some devices working while others don't — if Pi-hole DNS resolves for any device, the DNS layer is fine; connectivity issues are at WiFi/router/IP level (but FIRST rule out deep CNAME inspection false positives above)

## Router Investigation (When Pi-hole Is Ruled Out)

Check the ASUS router at 192.168.50.1:
1. **System Log → Wireless Log** — are affected devices connected?
2. **Network Map → Clients** — do they have valid IPs?
3. **WiFi Settings** — Smart Connect / band steering can confuse some devices. Try separating 2.4 GHz and 5 GHz SSIDs.

## Pi-hole v6 API (When You Have the Password)

```bash
# Get session
curl -s -X POST "http://192.168.50.X:8090/api/auth" \
  -H "Content-Type: application/json" \
  -d '{"password":"YOUR_PASSWORD"}'

# Get summary (after auth)
curl -s "http://192.168.50.X:8090/api/stats/summary" \
  -H "Authorization: Bearer SESSION_TOKEN"
```

## Other v6 Changes from v5

- `pihole -c` (chronometer) → removed, use PADD
- `/admin/api.php` → `/api` (different path, authentication required)
- `gravity.db` → replaced by `gravity` table in `pihole-FTL.db`
- Container has no Python or sqlite3 — query from host
