Diagnosing "connection refused" between Docker Compose services on a shared network
Two containers in the same docker-compose.yml, one can't reach the other, and the error
is some variant of connection refused or could not translate host name. This is almost
always one of four causes, in rough order of likelihood.
Cause 1: using localhost instead of the service name
Inside a container, localhost means that container, not the host machine or a sibling
container. If api tries to reach db via localhost:5432, it's asking its own container
for a database that isn't running there.
services:
db:
image: postgres:16
api:
build: ./api
environment:
# wrong: DATABASE_URL: postgres://user:pass@localhost:5432/app
DATABASE_URL: postgres://user:pass@db:5432/app # right — use the service name
Compose's built-in DNS resolves service names to the right container automatically, as long as both services are on the same network.
Cause 2: services aren't actually on the same network
By default, Compose puts every service in one project on a single shared network, so this
usually isn't the issue — until someone adds an explicit networks: block to one service
and forgets the other needs it too. If either service has its own networks: list, both
need to share at least one network:
services:
api:
networks: [backend]
db:
networks: [backend] # must match, or api can't resolve/reach db
networks:
backend:
Check with docker network inspect <project>_default (or your named network) — both
containers should be listed.
Cause 3: the target service is up but not ready yet
The container is running, but the process inside it (database, API server) hasn't finished
starting. depends_on alone only waits for the container to start, not the application
inside it to be ready — this is a distinct, common issue with its own fix (a healthcheck
plus depends_on: condition: service_healthy), which is common enough to warrant its own
writeup rather than a paragraph here.
Cause 4: wrong port, or expose vs published port confusion
expose makes a port reachable to other containers on the same network but not to your
host machine; ports: publishes it to the host too. Connection issues between containers
are essentially never fixed by adding a ports: mapping — that only affects host access.
Double check the port the target service actually listens on inside the container (not
the host-mapped port) matches what the caller is using:
services:
db:
image: postgres:16
expose: ["5432"] # reachable by other containers on this network
ports: ["5433:5432"] # reachable from the host at localhost:5433 — irrelevant to `api`
Quick checklist
- Is the caller using the service name, not
localhostor127.0.0.1? docker network inspect— are both containers on the same network?- Is the target's application actually finished starting (check its logs, not just
docker compose ps)? - Does the port match what's listening inside the container, not a host-mapped port?
Sources: Docker Compose networking documentation (service-name DNS resolution, default
network behavior); Docker Engine documentation on expose vs ports; verified against a
real multi-service Compose setup while building this project's automation.