16.2.2 App Connection Problems
A focused guide to App Connection Problems, connecting core concepts with practical Docker and container operations.
App connection problems describe a running container that is unable to establish or maintain a network connection to another service, whether a database, an internal microservice, or an external API, and effectively troubleshooting them requires working through the connection path in distinct layers, name resolution, network reachability, and the actual application protocol, rather than treating "can't connect" as a single, undifferentiated symptom.
Working through the layers in order
A connection failure can occur at several distinct points, and checking each one in sequence isolates exactly where the failure actually happens rather than guessing:
docker exec my-api nslookup my-db
docker exec my-api nc -zv my-db 5432
docker exec my-api curl -v http://my-api-2:8080/healthz
DNS resolution failing means the target hostname cannot be resolved to an address at all; a successful resolution but failed port connection means the network path exists but nothing is listening or reachable at that specific port; a successful port connection but a failed application-level response (a timeout or unexpected error at the protocol level) points toward a problem within the target service itself rather than networking at all.
DNS resolution failures
A hostname that fails to resolve inside a container most commonly indicates the two containers are not actually on the same Docker network, since Docker's embedded DNS only resolves service names for containers attached to the same user-defined network:
docker network inspect my-network --format '{{range .Containers}}{{.Name}} {{end}}'
docker exec my-api nslookup my-db
** server can't find my-db: NXDOMAIN
Confirming both containers actually appear in the same network's container list directly resolves whether this is the cause; a container attached to the default bridge network, rather than a user-defined one, does not get this automatic service-name DNS resolution at all, which is a frequent and simple cause of this specific failure for anyone not yet using an explicitly created network.
Port reachable but connection refused
A successful DNS resolution followed by an immediate "connection refused" indicates the target host is reachable at the network level but nothing is actually listening on the requested port, which is different from a timeout (discussed next) and points toward the target service either not having started yet, not listening on the expected port, or listening only on a more restrictive address:
docker exec my-db netstat -tlnp
tcp 0.0.0.0:5432 LISTEN
If the target service shows as listening on 0.0.0.0 (all interfaces) and the port number matches what is being requested, but the connection is still refused, double-checking that the client is actually targeting the correct port and hostname, rather than a typo or an outdated configuration value, is worth confirming directly.
Connection timeouts versus connection refused
A connection attempt that hangs and eventually times out, rather than failing immediately with "connection refused," generally indicates a firewall or network policy silently dropping the traffic rather than the target actively rejecting it, which is a meaningfully different category of cause:
docker exec my-api timeout 5 nc -zv my-db 5432
nc: my-db (172.18.0.5:5432): Operation timed out
This pattern is common when network isolation rules, such as those Docker's own network segmentation enforces between separate user-defined networks, are silently blocking traffic rather than the target service actively refusing it, which points the investigation toward network configuration and isolation policy rather than the target service's own listening behavior.
Network isolation between separate Docker networks
Containers on different Docker networks, even on the same host, cannot reach each other by default unless explicitly connected to a shared network, which is a deliberate isolation feature but a frequent source of confusion when two containers that are each individually working correctly cannot reach one another:
docker network connect shared-network my-api
docker network connect shared-network my-db
Explicitly connecting both containers to a common network resolves this directly; a container can be attached to multiple networks simultaneously, which is a common pattern for a service that needs to reach dependencies on more than one separately scoped network.
External connectivity issues
For connections to services outside the Docker host entirely, confirming basic outbound connectivity from within the container, separate from anything related to internal Docker networking, isolates whether the issue is container-specific or affects the host's outbound connectivity more broadly:
docker exec my-api curl -v https://api.external-service.com/health
docker exec my-api curl -v https://1.1.1.1
A failure reaching a known, generally reliable external endpoint (like a public DNS resolver's own IP) suggests a more fundamental outbound connectivity or DNS configuration problem affecting the container broadly, rather than something specific to the particular external service originally being investigated.
TLS and certificate-related connection failures
A connection that establishes at the network level but fails during a TLS handshake produces a distinct category of error, generally related to certificate validity, trust chain issues, or a hostname mismatch, rather than basic network reachability:
docker exec my-api curl -v https://internal-service.example.com
SSL certificate problem: unable to get local issuer certificate
This often indicates a missing or outdated CA certificate bundle inside the container's image, particularly relevant for minimal base images that may not include a full, current set of trusted root certificates by default, or for connections to internal services using certificates from a private, internal certificate authority not present in the image's default trust store at all.
Common mistakes
- Treating "can't connect" as a single symptom rather than working through DNS resolution, port reachability, and application-level response as distinct, separately diagnosable layers.
- Not checking whether two containers are actually attached to the same Docker network, a frequent and simple cause of DNS resolution failures between services.
- Confusing a connection timeout (often a silently dropped connection due to network policy) with connection refused (an active rejection from the target), which point toward different categories of cause.
- Assuming an external connectivity failure is specific to the originally investigated service without testing against a known, reliable external endpoint to rule out a more fundamental, broader networking issue.
- Overlooking outdated or missing CA certificates in a minimal base image as the cause of a TLS handshake failure that has nothing to do with basic network reachability.
App connection problems are most efficiently diagnosed by isolating which specific layer, DNS, network reachability, or application-level protocol response, is actually where the failure occurs, since each layer has a distinct, recognizable failure signature and a correspondingly distinct fix, rather than treating the symptom as a single, undifferentiated networking problem.