DockerIntermediate 12 min Lesson 6 of 10

Networks

How containers reach each other and the outside world: bridge networks, service names, published ports and isolation.

Docker · Lesson 6 of 10
0/10 done(0%)

What is it? #

Each container gets its own network namespace with its own IP address. Networks decide which containers can reach each other.

On a user-defined bridge network, containers can reach each other by name. Docker runs an internal DNS server that resolves container names to their addresses.

That naming is the key practical feature. An application connects to db:5432 rather than to an IP address that changes every time the container restarts.

Publishing a port is separate. It exposes a container port on the host, which is only needed for services the outside world must reach.

Think of it like this #

An internal office phone system. Extensions reach each other by name without going through reception.

Only the numbers printed on the door are reachable from outside, and most departments do not need one.

Simple example #

An application, a database and a cache on the same network. The application reaches the others by name, and only the application publishes a port to the host.

Code #

BASH
# A user-defined network gives you DNS between containers
docker network create shop-net

docker run -d --name db --network shop-net \
  -e POSTGRES_PASSWORD=secret postgres:16      # no published port needed

docker run -d --name cache --network shop-net redis:7

docker run -d --name app --network shop-net -p 8000:8000 \
  -e DATABASE_URL=postgresql://postgres:secret@db:5432/postgres \
  -e REDIS_URL=redis://cache:6379 \
  myapp:1.0

# The application resolves "db" and "cache" through Docker's DNS.
docker exec app getent hosts db
TEXT
Why the default bridge is different

Containers started with no --network join the default bridge, which has
NO automatic name resolution. They can reach each other by IP address
only, and those addresses change on restart.

Always create a user-defined network. It is one command and it is what
makes service names work.
BASH
# Inspecting
docker network ls
docker network inspect shop-net           # which containers, which addresses
docker exec app ping -c1 db
docker exec app nc -zv db 5432

# Connecting an existing container to another network
docker network connect other-net app
docker network disconnect other-net app
TEXT
Network modes

bridge (user-defined)   the normal choice: isolation plus DNS by name
host                    the container shares the host network stack: no
                        isolation, no port mapping, best performance
none                    no networking at all: for batch jobs that need none
container:<name>        share another container's network namespace

Reaching the host from inside a container:
  host.docker.internal        on Docker Desktop (macOS/Windows)
  the docker0 gateway IP      on Linux, or --add-host=host.docker.internal:host-gateway
TEXT
Isolation by design

Put only the containers that must talk to each other on the same network.
A public-facing proxy can sit on both a public network and an internal
one, while the database sits only on the internal one and is unreachable
from anything else.

How it works #

A user-defined bridge network creates a virtual switch. Containers attached to it get addresses on the same subnet and can reach each other directly.

Docker's embedded DNS resolves container and service names on that network. This is why connection strings use db rather than an IP — the name is stable, the address is not.

The default bridge deliberately lacks this DNS feature for backwards compatibility, which is the source of a common early confusion: containers that can ping by IP but not by name.

Publishing with -p creates a mapping on the host. Containers on the same network do not need it to talk to each other, which is why the database in the example publishes nothing — and should not.

That distinction is a security point. A published database port is reachable from outside the host; an unpublished one is reachable only by containers on its network.

host mode removes the network namespace entirely, giving the container the host's interfaces. It is faster and removes all isolation, so it is used for specific cases such as network monitoring tools.

Reaching the host from inside a container differs by platform, which is a frequent source of "works on my machine" differences between developer laptops and Linux servers.

Real-world use #

Compose creates a network automatically and attaches every service to it, which is why services in a Compose file can reach each other by service name with no configuration.

The isolation pattern — a proxy on two networks, backends on an internal one — is standard for multi-service deployments and mirrors the private networking approach on a VPS.

Not publishing database ports is the containerised equivalent of binding to localhost. Publishing 5432 on a public server exposes the database to the internet.

Name-based connection strings make environments portable: the same DATABASE_URL works in Compose, in CI and in an orchestrator, because the name is what is resolved.

In orchestrators the same idea scales up: service discovery resolves a service name to whichever instances are healthy, which is the model containers established.

Common mistakes #

  • Using the default bridge and finding that container names do not resolve.
  • Publishing database ports that only other containers need to reach.
  • Hardcoding container IP addresses, which change on restart.
  • Putting every container on one network when isolation was available.
  • Assuming host access works the same on Linux and Docker Desktop.

Practice #

Create a network with a database and an application container. Connect by service name and confirm it works, then try the same on the default bridge and observe the failure. Finally, confirm from the host that the unpublished database port is unreachable.

Quick quiz

  1. 1. What does a user-defined bridge network provide that the default does not?

  2. 2. Do containers on the same network need published ports to talk to each other?

  3. 3. Why should a database container not publish its port on a public server?

  4. 4. What does `--network host` do?

  5. 5. How should services address each other?

Summary

  • Create user-defined networks so containers resolve each other by name.
  • Publish ports only for services the outside world must reach.
  • Databases should be reachable by containers, not by the internet.
  • Use separate networks to isolate backends from public-facing services.
  • Never hardcode container IP addresses.