Search across all documentation pages
Create a network, attach containers, and reach them by name.
docker network create appnet
docker run -d --network appnet --name db postgres:16
docker run -d --network appnet --name api myapi:1.0
docker exec api getent hosts dbThe api container resolves db to its current IP through Docker's embedded DNS.
No published ports are needed for container-to-container traffic on the same network.
In practice you declare this in Compose, which creates a user-defined network for the project automatically.
services:
api:
image: myapi:1.0
environment:
DATABASE_URL: "postgres://app:secret@db:5432/app"
depends_on:
db:
condition: service_healthy
networks:
- backend
db:
image: postgres:16
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: secret
POSTGRES_DB: app
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app"]
interval: 5s
retries: 5
networks:
- backend
networks:
backend:The API connects to the host db - the service name - not to an IP address.
docker compose up -d
docker compose exec api ping -c1 dbBecause both services are on the backend network, db resolves and no port needs publishing.
Every container on a user-defined network gets /etc/resolv.conf pointed at 127.0.0.11.
That address is Docker's embedded DNS server, which answers with the current IP of any container name or network alias on the same network.
This is why a restarted container that gets a new IP is still reachable by name - the DNS record follows the name, not the address.
The default bridge network deliberately lacks this; it only supports the legacy --link flag, which you should not use.
In Compose, the service key (db, api) is the DNS name.
You can add extra names with aliases, useful when several services should answer to one logical name.
services:
primary:
image: postgres:16
networks:
backend:
aliases:
- dbNow both primary and db resolve to the same container.
Attaching a service to more than one network lets you split public and private traffic.
services:
web:
image: nginx:1.27
networks: [frontend, backend]
db:
image: postgres:16
networks: [backend]Here db is unreachable from anything on frontend alone, which is a simple, effective isolation boundary.
When you create a network, Docker's default IPAM driver allocates a subnet from a private pool.
You can pin it if you need to avoid overlap with an existing corporate range.
docker network create --subnet 172.28.0.0/16 --gateway 172.28.0.1 appnetPinning is useful in tightly managed environments, but for most apps the default allocation is fine and DNS makes the exact IPs irrelevant.
Container names must be unique per network for DNS to work; two containers cannot share a name.
The default bridge network gives no DNS, so docker run without --network will leave you unable to resolve names - always create or use a user-defined network.
DNS caching in some language runtimes pins the first resolved IP; if a dependency restarts and changes IP, long-lived clients may keep hitting the old address until they re-resolve.
depends_on controls start order, not readiness - use condition: service_healthy with a healthcheck so the API waits for the database to actually accept connections.
Compose prefixes networks with the project name, so backend becomes myproject_backend; reference it by the short name inside the file, but expect the prefixed name in docker network ls.
Default bridge with --link: deprecated and single-direction; avoid it entirely in favor of user-defined bridges.
Host networking: removes isolation and DNS but gives raw host performance; only for single, trusted, high-throughput containers.
Overlay networks: the multi-host answer for Docker Swarm, extending DNS-based discovery across nodes via VXLAN.
Kubernetes Services: the cluster-scale equivalent - a stable virtual IP and cluster DNS name in front of ephemeral Pods, which is the right tool once you move beyond one host.
Do I need to publish ports for containers to talk to each other? No. Publishing is only for reaching a container from outside the host; same-network containers talk directly.
Why does name resolution fail on the default bridge? The default bridge has no embedded DNS. Create a user-defined network and DNS works automatically.
Can two Compose projects share a network?
Yes, by declaring an external: true network that both projects reference by its real name.
What IP does the embedded DNS server use?
127.0.0.11 inside each container on a user-defined network.
Does this work across multiple hosts? Not with a plain bridge. Use an overlay network (Swarm) or Kubernetes Services for cross-host discovery.
Stack versions: This page was written for Kubernetes 1.36.2, Docker Engine 29.6.1 (BuildKit default), containerd (CRI runtime on nodes), Helm 3, Compose v2, Argo CD (latest - verify at build), and Gateway API (GA - verify controller support at build).
Reviewed by Chris St. John·Last updated Jul 16, 2026