# Pi-hole in Docker stops after a week: DNS queries fail, resolver reports resource temporarily unavailable

## TL;DR

Work outside-in. If the host itself cannot reach the internet by raw IP while the container is down, the failure is below Pi-hole, so check the host network first. If the host is fine, check whether FTL is still alive inside the container. The dockerd resolver line ending in resource temporarily unavailable is an EAGAIN class error, which on a week-long cycle most often means UDP conntrack entries or ephemeral source ports are exhausted, not that a setting changed. Prove which layer died before restarting anything, because a restart destroys the evidence.

## Verbatim report

```text
Secondary Pi-hole in Docker on Debian (Trixie) runs fine for about a week, then dies:
no DNS queries and no ping to 8.8.8.8. dockerd syslog shows a resolver connect
failed warning, dial udp to the host address on port 53, resource temporarily
unavailable. Container uses restart unless-stopped and FTLCONF dns listeningMode ALL.
```

## Steps

1. Test the host without DNS. From the host, ping a raw public IP such as 8.8.8.8 and open a plain IP connection. Success check: if raw IP traffic also fails, stop debugging Pi-hole and fix the host (NIC, default route, upstream router); if raw IP works and only name resolution fails, continue.
2. Check whether FTL died or the whole container did. Run docker ps to see uptime and restart count, then docker logs with a tail of the last 200 lines and look at the final FTL lines before the failure time. Success check: a restart count above zero or an FTL exit, crash, or out-of-memory line means the process died; a steady container with no FTL errors points at networking, step 3.
3. Check conntrack pressure on the host. Read the current count and the maximum from the nf_conntrack_count and nf_conntrack_max entries under /proc/sys/net/netfilter, and search the kernel log for table-full drop messages. Success check: a count sitting at or near the maximum confirms exhaustion; long-lived DNS resolvers accumulate UDP entries because every forwarded query holds one until timeout.
4. Check nothing else grabbed port 53 on the host. List UDP and TCP listeners on port 53 on the host (a stub resolver such as systemd-resolved is the usual squatter) and confirm the published container ports match the compose file, 53 on both protocols. Success check: exactly one owner per protocol on the host side, and it is the Docker proxy or the container.
5. Apply the fix that matches the proven layer. Conntrack full: raise nf_conntrack_max on the host and shorten the UDP conntrack timeout, then watch the count graph for a week. FTL crashing: pin the image to a named version instead of the latest tag so a silent recreate cannot change behaviour mid-week, and keep the log tail from the crash. Host network dying: fix at the host, not in compose. Success check for all branches: the resolver survives past the previous failure interval with the conntrack count well below the maximum.
6. While it is down next time, capture a debug log before restarting and note the exact failure time. Success check: you can line the failure time up against cron jobs, docker image pulls, DHCP renewals, and the conntrack graph, which is how a weekly cycle usually gives itself away.

## When this applies

- Dockerised Pi-hole, healthy for days, then total DNS failure until restarted.
- dockerd or syslog shows resolver connect failures ending in resource temporarily unavailable.
- The container restarts cleanly and works again for a similar interval.

## When this does not apply

- One client or one domain blocked while everything else resolves (a blocklist or per-client problem).
- Pi-hole web interface issues while DNS still answers.
- Bare-metal or VM installs with no Docker networking layer.

## Why it happens

A resolver that forwards every household query opens a large number of short UDP flows. Each one leaves a conntrack entry on the host until it times out, so a busy secondary resolver can fill the table on a slow weekly rhythm. When the table is full the kernel returns EAGAIN, which surfaces as resource temporarily unavailable, and new DNS flows fail first because they are the highest-rate traffic on the box. A restart clears the symptom, not the pressure, which is why the failure comes back on schedule.

## Edge cases

- Ping by raw IP failing at the same time always outranks the DNS theory; two failures starting together usually share a cause below DNS.
- A second Pi-hole for redundancy can mask the fault for days if clients fail over silently, so check which resolver each client actually used.
- Listening mode ALL plus published ports on a multi-interface host can answer on interfaces you did not intend; it is a security note, not the cause of a weekly death.

## Provenance

Resolved from the public thread: https://discourse.pi-hole.net/t/pihole-in-docker-stops/87928
