Docker Lesson 17 – Docker Networking Basics | Dataplexa
Section II · Lesson 17

Docker Networking Basics

Two containers are running on the same machine. Your API needs to talk to your database. By default, they can't reach each other — and that's intentional. Docker's networking model is isolated by design and open by configuration. This lesson teaches you how it works and how to connect what needs to be connected.

Networking is the part of Docker that trips up most developers because it's invisible — you can't see the network the way you can see a volume or a container. But once you understand the model, connecting containers becomes as natural as everything else.

Container Network Isolation

Every container gets its own network namespace — a completely isolated network stack with its own IP address, its own network interfaces, its own routing table, and its own set of ports. From inside a container, it looks like a separate machine on its own private network.

Two containers running on the same host cannot communicate with each other by default. This isolation is a security feature — a compromised container can't reach other containers on the same machine unless you explicitly configure it to. Communication has to be deliberately enabled.

The Apartment Building Analogy

Every container is like an apartment in a building. Each apartment has its own door, its own lock, and its own internal space — tenants can't walk into each other's apartments uninvited. To allow two tenants to communicate, you build a shared corridor between their apartments. That corridor is a Docker network. Containers on the same Docker network can talk to each other. Containers on different networks — or no network — cannot.

Docker's Built-in Networks

When Docker installs, it creates three default networks automatically. Every container you start is attached to one of these unless you specify otherwise.

docker network ls
# Lists all networks Docker knows about on this machine
NETWORK ID     NAME      DRIVER    SCOPE
3f8a2c1b4d7e   bridge    bridge    local
1a9b3c5d7e8f   host      host      local
2b4c6d8e0f1a   none      null      local

What just happened?

Three built-in networks appear immediately after installing Docker. bridge is the default — containers without an explicit network are attached here. It allows container-to-container communication via IP address but not by name. host removes network isolation entirely — the container shares the host's network stack directly. none disables all networking — the container has no network interface at all, used for maximum isolation. In practice, you'll create your own custom bridge networks rather than using the default one — and Lesson 18 covers exactly why.

The Default Bridge Network — and Its Limitation

The default bridge network connects all containers that don't specify a network. Containers on it can reach each other by IP address — but not by container name. That's the critical limitation.

IP addresses in Docker are assigned dynamically. Every time a container starts, it might get a different IP. If your API container hardcodes the database container's IP address, it breaks every time the database container restarts and gets a new IP. This is why the default bridge network is considered legacy — and why custom networks with DNS resolution are the correct approach for anything beyond a single throwaway container.

Default bridge network — IP only, no DNS

order-api
172.17.0.2
db-container
172.17.0.3
redis-cache
172.17.0.4
docker0 — default bridge (172.17.0.0/16)

Containers can ping each other by IP — but if db-container restarts and gets 172.17.0.5, the API breaks. No DNS. No name resolution.

Custom Networks — The Right Way

Custom bridge networks solve the DNS problem completely. Every container on a user-defined network can reach every other container on that network using its container name as a hostname. Docker runs a built-in DNS server that resolves container names automatically — no IP addresses required.

The scenario: You're a DevOps engineer setting up a local development stack with three containers — an API, a PostgreSQL database, and a Redis cache. All three need to communicate with each other. The API needs to reach the database by the hostname db-container and Redis by the hostname redis-cache. Static IPs would break on every restart — custom networks with DNS make this trivial.

# Step 1 — Create a custom bridge network
docker network create app-network
# Docker creates a new bridge network with its own subnet
# Containers on this network can reach each other by container name

# Step 2 — Start all three containers on the same network
docker run -d \
  --name db-container \
  --network app-network \
  -e POSTGRES_PASSWORD=secret123 \
  postgres:15-alpine

docker run -d \
  --name redis-cache \
  --network app-network \
  redis:7-alpine

docker run -d \
  --name order-api \
  --network app-network \
  -p 3000:3000 \
  order-api:v1.0.0
# All three containers are on app-network
# order-api can now connect to postgres at hostname "db-container"
# and to Redis at hostname "redis-cache" — Docker DNS handles the resolution
d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4
a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2
7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d

# Inside order-api container — DNS resolution works automatically:
# postgresql://db-container:5432/orders  ← connects to the Postgres container
# redis://redis-cache:6379               ← connects to the Redis container

What just happened?

All three containers started on app-network. Docker's built-in DNS automatically registered each container's name as a hostname on that network. The API can now connect to PostgreSQL using the connection string postgresql://db-container:5432/orders — the hostname db-container resolves to whatever IP address that container currently has, dynamically. Restart db-container and it gets a new IP — but the hostname still works. No hardcoded IPs, no broken connections after restarts. This is the fundamental advantage of custom networks over the default bridge.

Custom bridge network — DNS by container name

order-api
reaches others by name
db-container
hostname: db-container
redis-cache
hostname: redis-cache
app-network — custom bridge with built-in DNS

Container names become hostnames. DNS resolution is automatic. Restart any container and the name still resolves correctly.

Inspecting and Managing Networks

docker network ls                          # list all networks
docker network inspect app-network         # detailed view — which containers are connected
docker network connect app-network nginx-proxy    # add a running container to a network
docker network disconnect app-network nginx-proxy # remove a container from a network
docker network rm app-network              # delete a network (must have no connected containers)
docker network prune                       # remove all unused networks
NETWORK ID     NAME          DRIVER    SCOPE
3f8a2c1b4d7e   bridge        bridge    local
9e1f3a5b7c9d   app-network   bridge    local
1a9b3c5d7e8f   host          host      local
2b4c6d8e0f1a   none          null      local

[
  {
    "Name": "app-network",
    "Driver": "bridge",
    "Containers": {
      "7c8d9e0f...": { "Name": "order-api",    "IPv4Address": "172.18.0.4/16" },
      "d3e4f5a6...": { "Name": "db-container", "IPv4Address": "172.18.0.2/16" },
      "a1b2c3d4...": { "Name": "redis-cache",  "IPv4Address": "172.18.0.3/16" }
    }
  }
]

What just happened?

docker network inspect shows the full topology of the custom network — all three containers listed with their current IP addresses and names. This is how you debug connectivity issues: if a container isn't in the Containers list of the network you expect, it's not reachable by name on that network. The docker network connect command lets you attach a running container to an additional network — a container can be on multiple networks simultaneously, which is how you selectively expose some containers to the outside world while keeping others private.

The Default Bridge Network Has No DNS

A very common gotcha: two containers on the default bridge network cannot reach each other by container name — only by IP. Only user-defined custom networks get Docker's built-in DNS. Any time you need containers to talk to each other by name — which is almost always — create a custom network. The default bridge is fine for single containers that only need external access, nothing more.

Teacher's Note

In Section III you'll use Docker Compose, which automatically creates a custom network for every project and attaches all services to it — so you get DNS for free. But knowing how to do it manually makes you understand exactly what Compose is doing under the hood.

Practice Questions

1. The command used to create a new custom bridge network in Docker is called what?



2. To attach a container to a specific network when running docker run, which flag do you use?



3. To see all containers currently connected to a specific network and their IP addresses, which command do you run?



Quiz

1. Two containers are on Docker's default bridge network. The API container tries to connect to the database container using the hostname db-container but fails. Why?


2. An nginx proxy container needs to reach both a frontend container on frontend-net and a backend API on backend-net. How is this achieved?


3. A security-sensitive container must have absolutely no network access — it should be completely isolated from all networks. Which built-in Docker network achieves this?


Up Next · Lesson 18

Bridge Network

You've created a custom bridge network — now let's go deep on how it works internally, how traffic flows between containers, and how to use network aliases to decouple your containers from their names.