Multi-container apps (app + database + cache) depend on containers finding each other reliably.
Docker gives containers networking through network drivers:
- bridge: a private virtual network on one host. The default for containers.
- host: the container uses the host's network stack directly, with no isolation and no port mapping. Linux only, and occasionally used for performance.
- none: no networking at all.
- overlay (Swarm) and macvlan/ipvlan cover multi-host and LAN-attached setups.
Containers started without --network join the default bridge, where they can reach each other only by IP address. Don't use it for apps. Create a user-defined bridge network instead:
docker network create appnet
docker run -d --name db --network appnet postgres:17
docker run -d --name api --network appnet myapiOn a user-defined network, Docker's embedded DNS resolves container names (and --network-alias names) to their current IPs, so api connects to postgres://db:5432. IPs change when containers are recreated; names don't. Networks also isolate: containers on different networks can't talk unless one is attached to both (docker network connect).
Notice there's no -p on the database: container-to-container traffic flows over the network on the container's real port. Publish only what the outside world needs, such as the API or a reverse proxy.
Reaching the host from a container: Docker Desktop provides host.docker.internal; on Linux add --add-host=host.docker.internal:host-gateway.
docker network inspect appnet lists connected containers and their IPs. Docker Compose creates a network per project automatically, which is why services there can reach each other by service name.
Example
docker network create appnet
docker run -d --name db --network appnet -e POSTGRES_PASSWORD=dev postgres:17
docker run -d --name api --network appnet -p 8000:8000 \
-e DATABASE_URL=postgres://postgres:dev@db:5432/postgres myapi:1.0
# Name resolution from inside the network
docker run --rm --network appnet alpine nslookup db
# Name: db Address: 172.18.0.2
docker network inspect appnet --format '{{range .Containers}}{{.Name}} {{end}}'
# db api
# Reach a service running on the host (Linux)
docker run --rm --add-host=host.docker.internal:host-gateway alpine \
ping -c1 host.docker.internalRemember
- Use user-defined bridge networks; the default bridge has no name DNS
- Containers on the same network reach each other by name on container ports
- Only publish ports for traffic from outside Docker
- host.docker.internal reaches the host (host-gateway on Linux)
Watch out for
- Hard-coding container IP addresses — they change whenever containers are recreated.