Container-to-container communication over the bridge network, EXPOSE vs -p, and the volumes-vs-bind-mounts decision for persisting or sharing data.
Published September 23, 2026
By default, containers on the same user-defined bridge network (the default network Docker Compose creates for a project) can reach each other by SERVICE NAME, resolved via Docker's built-in DNS — a service named db in a compose file is reachable from another container simply as db:5432, no IP address needed and no manual /etc/hosts editing. This is what makes docker-compose-for-local-development's multi-container stacks (app + Postgres + Redis) work without any networking configuration beyond just naming the services.
EXPOSE 8080 # in the Dockerfile — purely documentation, doesn't publish anything
docker run -p 8080:8080 myapp # THIS actually maps host port 8080 to container port 8080
This is a common early confusion: EXPOSE in a Dockerfile documents which port the application listens on, but does NOT make it reachable from the host machine — that requires the -p host_port:container_port flag at docker run time (or the ports: section in a compose file). Two containers on the SAME bridge network can already reach each other on their internal ports without any -p mapping at all — -p is specifically about reaching a container FROM the host (or the outside world), not container-to-container communication.
# Named volume — Docker manages where the data actually lives
docker run -v pgdata:/var/lib/postgresql/data postgres
# Bind mount — maps a SPECIFIC HOST PATH directly into the container
docker run -v /Users/me/project/src:/app/src myapp
Both persist data outside the container's own writable layer (so it survives container removal), but serve different purposes:
Using a bind mount in production (tying a container to one specific host's exact filesystem layout) undermines container portability — the same image should run identically on any host, which is exactly what named volumes preserve and bind mounts don't.
Q: What happens to a named volume's data when the container using it is removed?
A: The volume itself persists independently of any specific container's lifecycle — docker rm removes the container but NOT the volumes it referenced (unless explicitly run with -v to also remove anonymous volumes); a new container can be started later referencing the SAME named volume and pick up exactly where the data left off.
Q: If two containers are on the same bridge network, do they still need any port mapping at all to talk to each other?
A: No — container-to-container communication on the same network happens directly over the container's actual listening port (e.g. db:5432), entirely independent of any -p host mapping; -p mappings only matter for traffic originating from OUTSIDE the Docker network (the host machine, or the internet).
Q: Why would EXPOSE even exist if it doesn't actually do anything functionally?
A: It serves as both documentation (a human or tool reading the Dockerfile immediately sees what ports the image is designed to listen on) and as a default for docker run -P (capital P, which auto-publishes all EXPOSEd ports to random host ports) — a minor convenience feature, distinct from the far more commonly used explicit -p mapping.
Q: Can a bind mount be used for something other than source code in local dev? A: Yes — a common additional use is mounting configuration files or credentials from the host for local testing without baking them into the image, though for anything sensitive in a real deployment, a proper secrets mechanism (Kubernetes Secrets, covered in Config & Secrets) is the correct production answer, not a bind-mounted file.