When the API cannot reach its database, check the network membership, the name and the listener, in that order. --network container:NAME starts a throwaway container inside another container's network namespace, bringing tools the database image lacks:
docker network inspect l3-booknest \
--format '{{range .Containers}}{{.Name}} {{.IPv4Address}}{{println}}{{end}}'
docker run --rm --network container:l3-db alpine:3 netstat -ltn | grep LISTEN
docker exec l3-api getent hosts postgres l3-dbl3-api 172.19.0.3/16 l3-db 172.19.0.2/16 tcp 0 0 127.0.0.11:42385 0.0.0.0:* LISTEN tcp 0 0 0.0.0.0:5432 0.0.0.0:* LISTEN tcp 0 0 :::5432 :::* LISTEN 172.19.0.2 postgres 172.19.0.2 l3-db
PostgreSQL 1,289 listens on all addresses; beside it is the embedded DNS server on 127.0.0.11, on a random port to which Docker 514 's rules redirect port 53. A name that does not resolve points to the default bridge or to containers on different networks; ECONNREFUSED means nothing listens yet, or only on 127.0.0.1; a timeout suggests an --internal network or a firewall rule. The community image nicolaka/netshoot (https://github.com/nicolaka/netshoot 11,018 ) packs tcpdump, dig, curl 3,008 , iperf3 and dozens more tools; run it with --network container:NAME as above. From the host, sudo nsenter -t PID -n enters a container's network namespace directly.