A single container is useful; containers talking to each other is where real applications live. An API needs its database, a web app needs its cache. This lesson explains how Docker networking works, how containers find each other by name, and how ports connect containers to the outside world.
List the networks Docker creates out of the box:
docker network lsYou will see three: bridge, host, and none.
Containers on the default bridge can reach each other only by IP address, which changes between restarts. Containers on a user-defined bridge network get something much better: automatic DNS. Every container can reach every other container on the same network by container name.
docker network create appnet
docker run -d --name db --network appnet -e POSTGRES_PASSWORD=secret postgres:16
docker run -d --name api --network appnet -p 3000:3000 my-apiInside the api container, the database is reachable simply as db:5432. No IP addresses, no configuration - the connection string is just:
postgres://postgres:secret@db:5432/postgres
This name-based discovery is the foundation Docker Compose builds on, and it is the pattern used in virtually every multi-container setup.
Container networks are private. To accept traffic from outside the host, publish a port with -p host:container:
docker run -d -p 8080:80 nginx # host 8080 -> container 80
docker run -d -p 127.0.0.1:5432:5432 postgres:16 # only local connections
docker run -d -p 80 nginx # random host port, see docker psTwo details worth remembering:
127.0.0.1 keeps a service private to the host machine - do this for databases in development so they are not exposed to your network.-p at all.docker network inspect appnet # who is connected, what IPs
docker exec api ping db # test name resolution and reachability
docker port api # show port mappingsYou can also connect and disconnect running containers without restarting them:
docker network connect appnet existing-container
docker network disconnect appnet existing-containerA container can sit on several networks at once, which is a simple way to segment traffic - for example, a backend network for api and db, and a frontend network for api and proxy, so the proxy can never reach the database directly.
Occasionally a container needs to call a service running on the host itself. Use the special name host.docker.internal (built into Docker Desktop; on Linux add --add-host=host.docker.internal:host-gateway).
docker run --add-host=host.docker.internal:host-gateway -it --rm alpine ping host.docker.internal-p host:container exposes services externally; bind to 127.0.0.1 to keep them local.Next up: Environment Variables and Configuration, where you will learn to configure the same image differently for dev, staging, and production.