You run a database migration script, start a local Docker container, or initiate an API call in Python, Go, or Node.js. Instead of establishing a connection, the runtime crashes instantly with a cryptic error:
socket.gaierror: [Errno 8] nodename nor servname provided, or not known
or in curl / Git on Linux:
curl: (6) Could not resolve host: api.example.com
fatal: unable to access 'https://github.com/org/repo.git/': Could not resolve host: github.com
Why does the error mention both "nodename" and "servname"? And why does a query work in your browser or with dig, but fail when invoked from inside an application, container, or CI/CD pipeline?
The answer lies in how operating systems and standard C libraries (glibc, musl, macOS libSystem) implement the POSIX getaddrinfo() system call. In modern network programming, hostname lookup is coupled with service/port translation and name service switch (nsswitch) pipelines.
In this deep-dive guide, we decode the POSIX getaddrinfo() resolver architecture, isolate nodename (host) vs servname (port/service) failures, troubleshoot Kubernetes ndots:5 query amplification, diagnose broken IPv6 Happy Eyeballs routing, and provide a definitive 10-step triage runbook.
1. Decoding nodename nor servname provided, or not known
When an application attempts to open a network socket to api.example.com:443, it does not immediately dispatch a DNS packet. It invokes the standard C library function getaddrinfo() (or the deprecated gethostbyname()).
int getaddrinfo(const char *node, // "nodename": Hostname or IP (e.g., "api.example.com")
const char *service, // "servname": Port or service name (e.g., "443" or "https")
const struct addrinfo *hints,
struct addrinfo **res);
THE POSIX getaddrinfo() RESOLUTION PIPELINE
Application Call: getaddrinfo("api.example.com", "https", ...)
│
┌───────────────┴───────────────┐
▼ ▼
1. Nodename Resolution 2. Servname Resolution
(Translates Host -> IP) (Translates Service -> Port)
│ │
├── Check /etc/hosts ├── Is it numeric string? ("443")
├── Query systemd-resolved └── Lookup /etc/services ("https" -> 443)
└── Dispatch UDP/TCP DNS │
│ ▼
▼ Port = 443 (htons)
IP = 198.51.100.10 │
│ │
└───────────────┬───────────────┘
▼
Returns struct addrinfo
(Ready for socket() & connect())
What the Error Codes Actually Mean
If getaddrinfo() fails, it returns a non-zero error constant defined in <netdb.h>:
| POSIX Return Code | Error String (macOS / BSD) | Error String (Linux / glibc) | Root Cause |
|---|---|---|---|
EAI_NONAME | nodename nor servname provided, or not known | Name or service not known | The hostname (node) does not resolve in DNS/hosts, OR an invalid service was supplied. |
EAI_SERVICE | servname not supported for ai_socktype | Servname not supported for ai_socktype | The service name does not exist in /etc/services for the requested socket type (SOCK_STREAM). |
EAI_AGAIN | Resource temporarily unavailable | Temporary failure in name resolution | DNS server timeout; network unreachable; DNS rate limit or packet loss. |
EAI_FAIL | Non-recoverable failure in name resolution | Non-recoverable failure in name resolution | Authoritative nameserver returned SERVFAIL or DNSSEC validation collapsed. |
2. Common Root Causes: Why It Breaks
Use this diagnosis table to classify why your specific environment is failing:
COMMON FAILURE MATRIX
┌─────────────────────────────────┬────────────────────────────────────────┐
│ Failure Vector │ Root Cause Mechanism │
├─────────────────────────────────┼────────────────────────────────────────┤
│ 1. URL Passed Instead of Host │ Passing "https://example.com" as node │
│ 2. Invalid Port/Service String │ Passing ":443" or "http//" as service │
│ 3. Trailing Spaces / Hidden CR │ Hidden "\r" from Windows .env files │
│ 4. Kubernetes ndots:5 Storm │ External domains queried 5x across K8s │
│ 5. Docker 127.0.0.11 Isolation │ Container lacks bridge DNS forwarder │
│ 6. Broken IPv6 (AAAA) Route │ AAAA resolves, but host lacks IPv6 exit│
│ 7. VPN Split-DNS Leak │ Corporate nameserver unreachable on tun│
└─────────────────────────────────┴────────────────────────────────────────┘
1. The "Full URL Passed as Hostname" Trap
The single most common developer error occurs when an application client (e.g. database driver, Redis client, or Python urllib) expects a pure hostname, but receives a full URI string:
# ❌ INCORRECT: getaddrinfo tries to look up the entire string as a DNS record!
db_host = "postgres://db.prod.internal:5432/main"
socket.getaddrinfo(db_host, 5432)
# -> socket.gaierror: [Errno 8] nodename nor servname provided, or not known
# ✅ CORRECT: Strip scheme, port, and path before passing to socket
db_host = "db.prod.internal"
socket.getaddrinfo(db_host, 5432)
2. The Windows CRLF (\r) Environment Variable Trap
When deploying applications from Windows to Linux Docker containers or CI/CD runners, .env files often retain Windows CRLF line endings.
- The environment variable becomes
DB_HOST="db.example.com\r". - The resolver attempts to query
db.example.com\rin DNS, which fails instantly withEAI_NONAME.
3. Step-by-Step Diagnostic Workflow
Follow this ordered diagnostic sequence to pinpoint where name resolution is breaking:
# Step 1: Validate direct DNS resolution using public resolvers
dig +noall +answer +comments api.example.com @1.1.1.1
# Step 2: Test resolution through the local OS resolver stack (glibc NSSwitch)
getent hosts api.example.com
# Step 3: Inspect local nameserver configuration
# On Linux:
cat /etc/resolv.conf
# On systemd-resolved systems:
resolvectl status
# On macOS:
scutil --dns
# Step 4: Test independent IPv4 and IPv6 connectivity
curl -4 -v https://api.example.com/health
curl -6 -v https://api.example.com/health
4. Reproducing & Testing with Python getaddrinfo()
Use this standalone Python script to test how the operating system's C library resolves your target host and service:
#!/usr/bin/env python3
import socket
import sys
def test_resolution(host, port_or_service):
print(f"[*] Testing resolution for host: '{host}', service: '{port_or_service}'")
try:
# Convert port to integer if numeric
service = int(port_or_service) if port_or_service.isdigit() else port_or_service
# Invoke POSIX getaddrinfo()
addr_info = socket.getaddrinfo(
host,
service,
family=socket.AF_UNSPEC, # Allow IPv4 (AF_INET) and IPv6 (AF_INET6)
type=socket.SOCK_STREAM # TCP connection
)
print(f"[+] Successfully resolved {len(addr_info)} address candidate(s):")
for idx, (family, socktype, proto, canonname, sockaddr) in enumerate(addr_info):
family_str = "IPv4" if family == socket.AF_INET else "IPv6"
print(f" [{idx + 1}] Family: {family_str:<4} | IP: {sockaddr[0]} | Port: {sockaddr[1]}")
except socket.gaierror as e:
print(f"[-] getaddrinfo FAILED: errno={e.errno} -> {e.strerror}")
sys.exit(1)
if __name__ == "__main__":
target_host = sys.argv[1] if len(sys.argv) > 1 else "example.com"
target_service = sys.argv[2] if len(sys.argv) > 2 else "443"
test_resolution(target_host, target_service)
5. Kubernetes & Docker DNS Resolution Issues
In containerized environments, DNS resolution failures have distinct architectural causes.
KUBERNETES ndots:5 QUERY AMPLIFICATION
Application in Pod queries "api.stripe.com" (Contains 2 dots: < 5 ndots)
│
Kubelet Appends Search Domains Sequentially:
├── 1. api.stripe.com.default.svc.cluster.local. ──► NXDOMAIN (CoreDNS)
├── 2. api.stripe.com.svc.cluster.local. ──► NXDOMAIN (CoreDNS)
├── 3. api.stripe.com.cluster.local. ──► NXDOMAIN (CoreDNS)
├── 4. api.stripe.com.us-east-1.compute.internal. ──► NXDOMAIN (VPC DNS)
└── 5. api.stripe.com. (Absolute FQDN query) ──► SUCCESS (200ms delay!)
1. The Kubernetes ndots:5 Latency & Exhaustion Trap
By default, Kubernetes configures /etc/resolv.conf inside pods with options ndots:5.
- Any domain with fewer than 5 dots (e.g.
api.example.comhas 2 dots) will search through all cluster search domains before attempting a direct external query. - This results in 4 failed
NXDOMAINqueries to CoreDNS for every 1 successful lookup, frequently exhausting CoreDNS CPU and triggering randomEAI_AGAIN/nodename nor servname providedtimeouts.
The Fix: Use trailing dots for external domains (e.g. api.example.com.) or tune pod dnsConfig:
spec:
dnsConfig:
options:
- name: ndots
value: "2"
2. Docker Embedded DNS (127.0.0.11) Stalls
In Docker user-defined bridge networks, Docker runs an embedded DNS server at 127.0.0.11.
- If the host machine changes network interfaces (e.g. connecting to or disconnecting from an OpenVPN/WireGuard tunnel), Docker's embedded DNS server continues forwarding queries to the previous, dead upstream resolver.
- Fix: Restart the Docker daemon (
sudo systemctl restart docker) or specify explicit fallback DNS flags:docker run --dns 1.1.1.1 --dns 8.8.8.8 my-app
6. IPv4 vs IPv6: The Broken AAAA Record Trap
A frequent cause of intermittent resolution and connection timeouts is asymmetric IPv6 configuration:
- An external service publishes both
A(IPv4) andAAAA(IPv6) records. getaddrinfo()returns both addresses, ordering IPv6 first (RFC 6724 address selection).- The local client attempts to connect to the IPv6 address.
- However, the host's ISP or cloud network has an unrouted or firewalled IPv6 gateway.
- The connection hangs until the TCP connection timeout expires before falling back to IPv4 (or fails completely if the client runtime does not implement RFC 8305 Happy Eyeballs).
# Test if IPv6 resolution is the specific point of failure
dig api.example.com AAAA +short
curl -6 -v --connect-timeout 3 https://api.example.com/ || echo "IPv6 Routing Failed"
7. Troubleshooting /etc/services & Servname Failures
If the error stems from the servname component, the port argument passed to the socket is invalid:
# Inspect registered service names in /etc/services
grep -E '^(http|https|postgres|ssh|redis)[[:space:]]' /etc/services
http 80/tcp www
https 443/tcp
ssh 22/tcp
postgresql 5432/tcp
Common Servname Mistakes:
- Passing
"http//"instead of"http"or"80". - Passing
"5432/tcp"instead of5432. - In stripped container base images (e.g. Alpine
scratchor minimal distroless images),/etc/servicesmay be completely absent. Passing named services like"https"fails withEAI_SERVICE. Always use numeric port integers (443) in automated production systems.
8. SRE Severity & Resolution Decision Tree
SRE RESOLUTION DECISION TREE
"Could not resolve hostname / nodename nor servname"
│
▼
Does "dig @1.1.1.1 <host>" return valid IP?
├── NO ──► Domain expired, typo in URL, or authoritative DNS down.
│ (Check domain registrar, WHOIS, and zone records)
│
└── YES
│
▼
Does "getent hosts <host>" work on the host?
├── NO ──► Local OS resolver misconfigured.
│ (Inspect /etc/resolv.conf, systemd-resolved, or VPN split-DNS)
│
└── YES
│
▼
Is the error occurring inside a Container / Kubernetes Pod?
├── YES ──► CoreDNS saturation, search domain loop, or ndots amplification.
│ (Check CoreDNS logs, pod ndots, and Docker 127.0.0.11)
│
└── NO ──► Application syntax error: URI scheme embedded in host string,
or hidden CRLF (
) line ending in environment variables.
9. SRE Production Checklist for DNS Reliability
- Input Sanitization: Application connection strings strictly parse hostnames independently from URL schemes (
https://), ports (:443), and paths (/v1). - Numeric Ports in Production: Use integer port values (
443,5432) rather than named string protocols (https,postgresql) to avoid/etc/servicesdependencies. - Kubernetes
ndotsOptimization: High-throughput pods targeting external APIs usendots:2or fully qualified domain names with trailing dots (api.stripe.com.). - Redundant Upstream Resolvers:
/etc/resolv.confdeclares at least two diverse Anycast nameservers (1.1.1.1,8.8.8.8). - IPv6 Happy Eyeballs Validation: Verify that dual-stack hosts have functional IPv6 default gateways or configure runtimes to prefer IPv4 if IPv6 egress is disabled.
Conclusion & Next Steps
The error nodename nor servname provided, or not known is not a mysterious kernel fault—it is the direct output of POSIX getaddrinfo() failing to map a hostname or service string to an actionable IP socket.
By verifying hostname inputs, isolating local OS resolvers from public recursive lookup paths, tuning container search domains, and standardizing on numeric ports, you can eliminate name resolution errors across your distributed architecture.
Audit and Test Live DNS Resolution with Pingzoapp
Don't wait for application crashes to find out your external dependencies, domain records, or nameservers are failing.
With Pingzoapp, you get:
- Global Multi-Region DNS Probing: Monitor DNS lookup latencies,
A/AAAArecord changes, and nameserver health across multiple continents. - Interactive DNS Diagnostic Tools: Test and debug records in real time using our free DNS Lookup Tool.
- Instant Multi-Channel Alerts: Get notified via WhatsApp, Telegram, SMS, Slack, and Discord the second an endpoint fails name resolution.
👉 Start Monitoring DNS Free with Pingzoapp and safeguard your infrastructure against silent resolution outages.
Stop Finding Out About Outages from Angry Users
Get instant WhatsApp & Discord alerts the second your API, website, or server goes down. Setup in 30 seconds with 60-second checks.