Matched signals
- Network unreachable
- connect: Network is unreachable
- ECONNREFUSED.*::1
- getaddrinfo ENOTFOUND
- curl.*Could not resolve host
- prefer-ipv4
- prefer-ipv6
- ADDRESS_FAMILY_NOT_SUPPORTED
IPv6 vs IPv4 DNS resolution failure in CI container
What this failure means
CI container or test environment fails to connect because the system
resolves a hostname to an IPv6 address (AAAA record) but the container
network does not support IPv6, or a service binds on ::1 (IPv6 loopback)
when the client connects over IPv4.
Symptoms
Faultline looks for one or more of these log fragments:
Network unreachable
connect: Network is unreachable
ECONNREFUSED.*::1
getaddrinfo ENOTFOUND
curl.*Could not resolve host
prefer-ipv4
prefer-ipv6
ADDRESS_FAMILY_NOT_SUPPORTED
Diagnosis
Docker containers have IPv6 disabled by default unless explicitly enabled
in daemon.json. When a hostname resolves to an AAAA record first and the
container network offers no IPv6 route, connections fail immediately.
Common scenarios:
- An integration test service binds on
0.0.0.0but the test opens a connection to::1orlocalhostresolves to::1 - A container DNS resolver returns AAAA records, the client tries IPv6,
and the kernel returns
EAFNOSUPPORT - A service listens on
tcp6and the client connects IPv4-only
Investigate:
# Check current address families
docker run --rm alpine cat /etc/hosts
# ::1 vs 127.0.0.1 for localhost
# Check what address a hostname resolves to inside the container
docker exec <container> getent ahosts registry.npmjs.org
# Check kernel IPv6 support
docker exec <container> cat /proc/sys/net/ipv6/conf/all/disable_ipv6
Fix steps
-
Force IPv4 resolution for tools that try IPv6 first:
# curl curl -4 https://registry.npmjs.org # Node.js 17+ prefers IPv6 by default; revert to v4-first export NODE_OPTIONS="--dns-result-order=ipv4first" # Python import socket socket.setdefaulttimeout(10) # Or use paramiko/requests with source_address bound to an IPv4 addr -
Enable IPv6 in Docker if services genuinely need it:
// /etc/docker/daemon.json { "ipv6": true, "fixed-cidr-v6": "2001:db8:1::/64" }sudo systemctl reload docker -
Bind services to
0.0.0.0and not::1to avoid IPv6-only sockets:// BAD: Go net.Listen — ":8080" binds both IPv4 and IPv6 on most systems // but "[::]" or "[::1]" is IPv6-only ln, _ := net.Listen("tcp", "[::1]:8080") // IPv6 only // GOOD: explicit dual-stack or IPv4 ln, _ := net.Listen("tcp", "0.0.0.0:8080") -
Disable IPv6 resolution in the container if not needed:
# In the CI job, before running services echo "net.ipv6.conf.all.disable_ipv6 = 1" | sudo tee -a /etc/sysctl.conf sudo sysctl -p # Or update /etc/hosts to remove ::1 for localhost sudo sed -i '/^::1.*localhost/d' /etc/hosts -
GitHub Actions: hosted runners have IPv6 disabled; if relying on Docker services, specify
127.0.0.1overlocalhost:services: redis: image: redis:7 ports: - "127.0.0.1:6379:6379" # explicit IPv4 bind
Validation
- Run
getent ahosts <hostname>inside the container to confirm IPv4 resolution. - Run
nc -zv 127.0.0.1 6379to confirm the service is reachable on IPv4. - Re-run the failing test/job.
Why it matters
IPv6 vs IPv4 mismatches cause silent or misleading connection failures that look like service-not-started errors. They are environment-specific (passes on developer machine with IPv6 enabled, fails on CI runner without) and hard to reproduce.
Prevention
- Bind services explicitly to
0.0.0.0rather thanlocalhostor::. - Specify
127.0.0.1when mapping ports in Docker service definitions. - Set
NODE_OPTIONS=--dns-result-order=ipv4firstin CI environments until full IPv6 support is confirmed.
How Faultline detects it
Use faultline explain ipv6-ipv4-resolution to see the full playbook.
faultline analyze build.log
faultline explain ipv6-ipv4-resolution
Generated from playbooks/bundled/log/network/ipv6-ipv4-resolution.yaml. Do not edit directly.