Automatic DNS — No IP Addresses Required
Networking is one of the most powerful and underappreciated features of Docker Compose. When you run docker-compose up, Compose automatically creates a dedicated virtual bridge network for your project (named <project-name>_default, where the project name defaults to the directory name). Every service defined in your file is attached to this network automatically.
More importantly, Compose configures internal DNS so that each service is reachable by its service name as a hostname. In the three-tier example above:
- The
webcontainer connects to Postgres atdb:5432— not172.18.0.3:5432. - The
webcontainer connects to Redis atredis:6379. - No IP addresses, no
/etc/hostsediting, no--linkflags.
This is why the DATABASE_URL in the example above uses @db:5432 — db is the service name, and Compose's DNS resolves it to the correct container IP automatically.
Custom Networks for Isolation
For more complex applications, you can define multiple networks to control which services can talk to each other. For example: a web service attached to both a frontend and a backend network, an api service attached only to backend, and a db service also attached only to backend.
In this setup, web is on both frontend and backend networks, so it can reach both api and db directly. Meanwhile, api and db are only on the backend network, so they cannot be reached from any service that is exclusively on the frontend network. If you added a dedicated reverse-proxy service attached only to frontend, it would be able to reach web but not api or db — that is the segmentation this pattern provides.
A common interview question: "How does service discovery work in Docker Compose?" The answer: Compose creates a user-defined bridge network and registers each service name as a DNS entry within that network. Containers resolve service names to container IPs via Docker's embedded DNS server (running at 127.0.0.11 inside each container). This is fundamentally different from the deprecated --link flag, which injected /etc/hosts entries and had no dynamic update capability.