Skip to content

fix: resolve github.com reliably via explicit DNS upstreams - #34

Merged
ChrisSc merged 1 commit into
mainfrom
fix/container-dns-upstreams
Oct 8, 2026
Merged

ChrisSc merged 1 commit into
mainfrom
fix/container-dns-upstreams

Conversation

@ChrisSc

@ChrisSc ChrisSc commented Oct 8, 2026

Copy link
Copy Markdown
Owner

Problem

Inside claude-code, github.com failed to resolve (Could not resolve host) while every other GitHub host (api., codeload., objects.githubusercontent.com, …) worked. The firewall log showed the same thing at boot: could not resolve github.com.

Root cause

On Docker Desktop, the embedded resolver forwards to host(192.168.65.7), which goes through the macOS system resolver (mDNSResponder). On a lossy link, that layer caches dropped upstream queries as negative answers:

Path NXDOMAIN Timeouts
Mac → 8.8.8.8 / 8.8.4.4 (UDP + TCP) 0 / 120 ~10–15%
Container → 8.8.8.8 directly 0 / 40 9 / 40
Container → 127.0.0.11 (Docker → host resolver) 38 / 40 2 / 40

dns-sd -G v4 github.com on the host flipped between real A records and No Such Record. The bad answer comes from the resolver layer, not from the network. github.com has a 60s TTL and is re-queried constantly, so it hits this far more than long-TTL hosts. A timeout gets retried; a cached NXDOMAIN fails immediately.

Fix

  • compose.yaml: dns: [8.8.8.8, 8.8.4.4] on claude-code, so the embedded resolver queries Google directly instead of the host resolver cache. 127.0.0.11 stays the container's nameserver.
  • init-firewall.sh: explicit upstreams are dialed from inside the container netns (unlike host(x) upstreams). Without an allow, strict mode's OUTPUT DROP turned every lookup into SERVFAIL (reproduced). The script now allows udp+tcp/53 to the plain # ExtServers: entries Docker writes into resolv.conf. DNS to any other destination stays dropped, and these are the same recursive resolvers 127.0.0.11 already forwards to, so there's no new tunnel surface. It also adds a post-clamp DNS check, so a resolver failure isn't misreported as "GitHub ranges may have failed to load".
  • Docs: the DNS invariant in .devcontainer/CLAUDE.md; the symptom in README troubleshooting.

Testing

  • Throwaway container from the built image, strict mode, patched script:
    • --dns 8.8.8.8 --dns 8.8.4.4: getent github.com 60/60 (was 0/10); git ls-remote https://github.com/... OK; example.com and direct DNS to 1.1.1.1 still blocked; re-run is idempotent.
    • Default (host() upstream): no extra rules added, behavior unchanged.
  • Live container, rebuilt and recreated: make boot-check OK, healthy, ExtServers: [8.8.8.8 8.8.4.4], getent github.com 40/40, git over HTTPS and SSH to GitHub OK.
  • make lint gates: shellcheck, hadolint, yamllint (via uvx), docker compose config — all pass. The one yamllint warning (mem_limit comment) predates this change.

Notes

  • 8.8.8.8 / 8.8.4.4 are now hardcoded; a network that blocks Google DNS would need these edited.
  • The host itself still has the flaky resolver (it bit git push while opening this PR). This fixes the container only.

🤖 Generated with Claude Code

On Docker Desktop the embedded resolver (127.0.0.11) forwards through the
host OS resolver, which on a lossy link caches dropped upstream queries as
NXDOMAIN. github.com (60s TTL, re-queried constantly) then fails with
"Could not resolve host" while long-TTL GitHub hosts keep working. Measured:
0/40 container lookups succeeded, while 8.8.8.8 queried directly never
returned NXDOMAIN (0/160), only ~10-15% timeouts.

- compose: set dns: [8.8.8.8, 8.8.4.4] on claude-code so the embedded
  resolver queries Google directly instead of the host resolver cache.
- init-firewall.sh: explicit upstreams are dialed from inside the container
  netns, so strict mode's OUTPUT DROP turned every lookup into SERVFAIL.
  Allow udp+tcp/53 to the plain (non-host()) ExtServers Docker records in
  resolv.conf; DNS to any other destination stays dropped. Add a post-clamp
  DNS check so a resolver failure isn't misreported as missing GitHub ranges.
- Document the invariant (.devcontainer/CLAUDE.md) and the symptom (README).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ChrisSc
ChrisSc merged commit 0feb7ef into main Oct 8, 2026
4 checks passed
@ChrisSc
ChrisSc deleted the fix/container-dns-upstreams branch October 8, 2026 03:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant