Network troubleshooting

A method for "it can't connect".

Intermediate10 min · lesson 15 of 30

Container connectivity problems yield to a method, not guesswork: work outside-in and stop at the first “no.” Is the container running? Is it attached to the network you think? Does the name resolve? Is the target port actually open and listening? Is it published, if you are reaching from the host? Each question has a one-line check, and the first that fails is your answer.

OUTSIDE-IN: STOP AT THE FIRST 'NO'
1Container running?
docker ps / docker inspect
2On the right network?
network inspect — are both actually attached?
3Name resolves?
nslookup db (user-defined net has DNS)
4Port open & listening?
nc -zv db 5432; ss -tlnp inside the target
5Published to host?
docker port — only when reaching from the host
Each question is a one-line check; the first that fails is your answer — usually the two containers are on different networks.
terminal
$ docker inspect -f '{{ json .NetworkSettings.Networks }}' web # which networks + IP?
$ docker network inspect appnet -f '{{ range .Containers }}{{ .Name }} {{ end }}'
web db # are BOTH actually attached to appnet?

A toolbox on the network

Minimal images lack ping, curl, and dig, so the fastest move is a throwaway toolbox (nicolaka/netshoot) on the same network. nslookup a name to test DNS, curl the name and port to test reachability, and ss inside the target to see whether the app is even listening. This cleanly separates “DNS is broken” from “the port is closed” from “the app is not listening at all.”

terminal
$ docker run --rm -it --network appnet nicolaka/netshoot
# nslookup db # DNS: does the name resolve to an IP?
# nc -zv db 5432 # reachability: is the port open?
# curl -m3 http://web:3000/healthz

Scenario: "web cannot reach db"

The single most common failure. Walk it: are web and db on the same user-defined network (not the default bridge, which has no DNS)? Does nslookup db resolve? Does the port connect? Is db actually listening? Nine times out of ten the answer is that the two containers are on different networks, so the name never resolves.

terminal
$ docker inspect -f '{{range $k,$v := .NetworkSettings.Networks}}{{$k}} {{end}}' web
appnet
$ docker inspect -f '{{range $k,$v := .NetworkSettings.Networks}}{{$k}} {{end}}' db
bridge # <- there it is: db is on the default bridge, not appnet
$ docker network connect appnet db # fix: put db on the same network

Scenario: "cannot reach my published port from the host"

The container is up and -p looks right, but curl localhost:8080 refuses. First confirm the mapping with docker port; then check the sneaky one — the app inside the container bound to 127.0.0.1 instead of 0.0.0.0, so it is listening only to itself and no host mapping can ever reach it. ss inside the container shows the truth.

terminal
$ docker port web
8080/tcp -> 0.0.0.0:8080 # mapping is fine
$ docker exec web ss -tlnp
State Local Address:Port
LISTEN 127.0.0.1:80 # the bug: bound to localhost, not 0.0.0.0
# fix the app config to listen on 0.0.0.0 and it becomes reachable
Bind the app to 0.0.0.0, not localhost
An application configured to listen on 127.0.0.1 inside a container is reachable only from within that container — publishing the port with -p still fails, because nothing is listening on the container’s external interface. The app must bind 0.0.0.0 (all interfaces) to be reachable at all; this is one of the most common “the port is mapped but nothing answers” causes.