Skip to main content

Lesson: How Containers Talk

What you'll learn

  • Describe the three core Docker network modes: bridge, host, and none.
  • Publish a container port to the host with -p and read the HOST:CONTAINER syntax correctly.
  • Connect containers on a user-defined network so they reach each other by name (built-in DNS).
  • Connect container networking back to the IP/ports/DNS basics from Module 1.

By the end you can wire up containers so they can be reached from outside and talk to each other reliably — the networking skill every multi-service app depends on.

The lesson

1. A quick callback to Module 1

In Module 1 you learned that a service is reachable at an IP address + port, and that DNS turns names into IP addresses. Container networking is exactly those ideas applied inside a host. Each container gets its own IP (in its own network namespace), services listen on ports, and Docker provides a little DNS server so containers can find each other by name. Keep that IP + port + DNS triangle in mind.

2. The default: bridge network

When you install Docker, it creates a virtual network called bridge (a software switch named docker0 on the host). By default every container you start joins it and gets a private IP like 172.17.0.x. Containers on the bridge can reach the outside world (the host NATs their traffic out), but the outside world cannot reach in unless you publish a port.

        HOST (e.g. lab VM)
   +-----------------------------------+
   |   docker0 bridge  172.17.0.0/16   |
   |    |              |               |
   |  [c1 .0.2]     [c2 .0.3]          |
   +-----------------------------------+
            |  (NAT out to the network / internet)
            v

List networks any time:

docker network ls

3. Publishing ports with -p

A container's port is private to its network. To let the host (and anything that can reach the host) connect, you publish it:

docker run -d --name web -p 8080:80 nginx:1.27

Read -p HOST:CONTAINER left-to-right: host port 8080 forwards to container port 80. Now a browser hitting the host on 8080 reaches nginx. You can:

-p 8080:80          # host 8080  -> container 80
-p 127.0.0.1:8080:80  # only on the host's loopback (not exposed to the network)
-p 80:80            # host 80    -> container 80

Two containers cannot both publish the same host port — that port is a single host resource. Choose distinct host ports.

4. host and none modes

Besides bridge, two other modes are worth knowing:

  • host (--network host): the container skips its own network namespace and shares the host's network directly. No -p needed — a container listening on 80 is on the host's 80. Faster, but no isolation and easy port clashes. Use sparingly.
  • none (--network none): the container gets no networking at all. Useful for a job that should be fully offline (e.g. processing a local file).
docker run --rm --network host nginx:1.27   # shares host net
docker run --rm --network none alpine ip a  # only loopback exists

5. The key skill: user-defined networks + DNS

The default bridge does not give containers name-based discovery. The professional pattern is to create your own user-defined bridge network. On a user-defined network, Docker runs an embedded DNS server: every container is reachable by its container name as a hostname. No hard-coded IPs.

# 1. Create a network
docker network create appnet

# 2. Start a database on it
docker run -d --name db --network appnet \
  -e POSTGRES_PASSWORD=<REDACTED> postgres:16

# 3. Start an app on the SAME network
docker run -d --name api --network appnet -p 8080:8080 myorg/api:sha-abc123

Now inside the api container, the database is reachable simply as the hostname db on port 5432 — Docker's DNS resolves db to the database container's current IP automatically.

            appnet  (user-defined bridge, with DNS)
   +-----------------------------------------------+
   |   [api]  --- resolves "db" ---> [db]          |
   |    :8080                          :5432        |
   +-----------------------------------------------+
         |
       -p 8080:8080 (only the api is published to the host)

This is why your app config should say db:5432, not 172.17.0.3:5432 — IPs change when containers restart, names do not. Compose (Chapter 4) automates exactly this: it creates a network and puts every service on it so they reach each other by service name.

6. Inspecting and troubleshooting

When something cannot connect, inspect the wiring:

# Which network is a container on, and what is its IP?
docker inspect web --format '{{json .NetworkSettings.Networks}}'

# Who is attached to a network?
docker network inspect appnet

# Test DNS / reachability FROM inside a container
docker exec -it api sh
#   then inside:  ping db    /    wget -qO- http://db:5432   (or curl)

Common gotchas:

  • Trying to reach a container by name on the default bridge — DNS only works on user-defined networks. Fix: create and use your own network.
  • Container "works" but is unreachable from your laptop — you forgot -p, or published to 127.0.0.1 only.
  • Two containers fighting over the same host port — change one -p host side.

7. Mental model to keep

  • Inside a network, containers talk to each other by name on the container port (e.g. db:5432).
  • From outside, you reach a container only through a published host port (-p). Keep those two paths separate in your head and most networking confusion disappears.

Dig deeper

Search terms

  • docker bridge vs host vs none network
  • docker -p port publishing explained
  • docker user-defined network container dns by name
  • docker network create connect containers
  • docker container cannot reach other container by name

Check yourself

  1. What does -p 8080:80 mean — which side is the host, which is the container?
  2. Why can't containers on the default bridge resolve each other by name, and what fixes that?
  3. On a user-defined network named appnet, how does the api container reach a db container listening on 5432?
  4. What does --network host change, and what is the downside?
  5. Why should an app's config reference db:5432 instead of a hard-coded IP like 172.17.0.3:5432?