Module P-2·25 min read

Bridge vs Host networks, port publishing, internal DNS resolution, and troubleshooting connectivity.

Introduction

A container running in absolute isolation is useless. It needs to accept incoming HTTP requests, query databases, and talk to other microservices.

However, because Docker relies on Linux Network Namespaces, every container has its own isolated network stack by default. Getting packets in and out of that isolated stack requires understanding Docker's networking models.

In this module, we will explore the three primary network drivers (bridge, host, and none), understand the critical difference between exposing and publishing ports, and see how containers resolve each other via internal DNS.


The Three Primary Network Drivers

When you launch a container, you assign it to a network. If you don't specify one, Docker assigns it to the default bridge network.

1. The none Driver (Total Isolation)

If you start a container with --network none, Docker provisions a Network Namespace but does not configure any interfaces inside it (except the loopback interface, localhost).

The container has no external IP address, cannot reach the internet, and cannot be reached by other containers. It is entirely air-gapped. This is rarely used in typical web applications but is useful for highly secure, localized data-processing containers.

2. The host Driver (No Isolation)

If you start a container with --network host, Docker disables the Network Namespace entirely.

The container shares the exact same network stack as the host operating system. If your Node.js application listens on port 3000 inside the container, it is literally binding to port 3000 on the host machine.

  • Pros: Maximum performance. There is no Network Address Translation (NAT) overhead.
  • Cons: Security and port conflicts. If two containers try to bind to port 3000 using --network host, the second one will crash with an EADDRINUSE error.

[!NOTE] The host networking driver only works on native Linux hosts. It does not work as expected on Docker Desktop for macOS or Windows because the "host" in that context is the hidden Linux VM, not your physical Mac/PC.

3. The bridge Driver (Default & Most Common)

The bridge network is a software-defined switch that sits inside the Linux kernel.

When you use the bridge driver (the default), Docker assigns the container an internal private IP address (e.g., 172.17.0.2). All containers attached to the same bridge network can communicate with each other using these internal IPs.

The bridge is connected to the host's physical network interface via NAT. This allows containers to make outbound requests to the public internet, but prevents the outside world from reaching in—unless you explicitly publish a port.


Exposing vs. Publishing Ports

This is one of the most confusing concepts for Docker beginners.

EXPOSE (Documentation Only)

In your Dockerfile, you might write:

dockerfile
EXPOSE 3000

This does absolutely nothing at runtime. It does not open a firewall rule. It does not map a port. It is purely documentation for the developer, stating: "This application expects to listen on port 3000."

Publishing (-p or --publish)

To actually allow traffic from the host machine to reach the container on a bridge network, you must publish the port at runtime using the -p flag:

bash
docker run -p 8080:3000 my-node-api

This syntax is HOST_PORT:CONTAINER_PORT.

Docker creates an iptables rule on the Linux host. When traffic hits port 8080 on the host machine, Docker's NAT intercepts it and forwards it to port 3000 on the container's isolated internal IP address.

[!CAUTION] If you run docker run -p 3000:3000, Docker binds to 0.0.0.0:3000 on the host. This means the port is exposed to the entire public internet if the host does not have a separate firewall! To bind only to the local machine, use -p 127.0.0.1:3000:3000.


Container-to-Container Communication (DNS)

In a microservices architecture, your Node.js API container needs to talk to your PostgreSQL database container.

You should never use internal IP addresses (like 172.17.0.3) because containers are ephemeral. If the database container restarts, it will get a new IP address, and your API will break.

Instead, you use User-Defined Bridge Networks and Docker's internal DNS resolver.

1. Create a custom network

bash
docker network create my-app-network

2. Launch the database

bash
docker run -d --name postgres-db --network my-app-network postgres:15

3. Launch the API

bash
docker run -d --name node-api --network my-app-network \ -e DATABASE_URL="postgres://user:pass@postgres-db:5432/mydb" \ my-node-api

Notice the DATABASE_URL. The host is postgres-db.

Because both containers are on the same user-defined network (my-app-network), Docker runs an internal DNS server. When node-api tries to resolve the hostname postgres-db, Docker dynamically returns the correct internal IP address of the database container, even if it changes!

[!IMPORTANT] The default bridge network (named bridge) does not support automatic DNS resolution. You must create a custom user-defined network to get hostname resolution. This is a common pitfall!


Key Takeaways

  1. Host vs Bridge: The bridge network provides isolation with NAT overhead. The host network disables isolation for maximum performance but risks port conflicts.
  2. Publishing Ports: The EXPOSE instruction is just documentation. You must use -p HOST:CONTAINER to actually route external traffic into the container.
  3. Internal DNS: Never use IP addresses to connect containers. Place them on a custom network and use their container names (--name) as hostnames.
  4. Security: By default, -p binds to 0.0.0.0. Be careful not to accidentally expose private databases to the public internet.

Knowledge Check

Question 1: You have an Express API and a Redis cache. How should the Express API connect to the Redis container?

  • A) By using -p 6379:6379 on the Redis container and connecting via localhost:6379.
  • B) By inspecting the Redis container to find its 172.17.0.x IP address and hardcoding that into the Express .env file.
  • C) By placing both containers on a user-defined network and connecting via the Redis container's name (e.g., redis://my-redis-container:6379).
  • D) By using the none network driver for maximum security.
Reveal Answer

Correct Answer: C

User-defined networks provide built-in DNS resolution. By connecting both containers to the same custom network, the Express container can resolve the hostname my-redis-container to the correct internal IP automatically. Option A routes traffic out to the host and back in (inefficient and potentially insecure), and Option B is brittle because IPs change when containers restart.


Question 2: A junior developer adds EXPOSE 5432 to the Dockerfile of a custom PostgreSQL image, but complains that they still cannot connect to the database from their host machine using a local pgAdmin client. What is the technical reason for this failure?

  • A) EXPOSE only works for HTTP/TCP traffic on port 80 or 443; database ports require different instructions.
  • B) EXPOSE is purely documentation and does not actually modify the host's iptables rules or route traffic; the port must be explicitly published at runtime using -p.
  • C) The container must be run on the host network for the EXPOSE directive to take effect.
  • D) The developer needs to use PUBLISH 5432 in the Dockerfile instead of EXPOSE.
Reveal Answer

Correct Answer: B

The EXPOSE instruction in a Dockerfile serves as documentation for developers and automated tools (like reverse proxies), signaling which port the application inside the container intends to listen on. However, it does not actually open any firewall rules or establish NAT routing. To access the container from the host machine, you must explicitly publish the port at runtime using the -p 5432:5432 flag.


Question 3: What is the critical security implication of running a container with the command docker run -p 6379:6379 redis on a public-facing cloud server (like an AWS EC2 instance or DigitalOcean droplet) that does not have a separate external cloud firewall configured?

  • A) The container will be completely isolated and unable to access the public internet to download updates.
  • B) Docker binds the published port to 0.0.0.0 by default, exposing the unauthenticated Redis database directly to the entire public internet.
  • C) The host network driver is automatically engaged, granting the Redis process root-level filesystem access to the server.
  • D) Traffic will be routed successfully, but the connection will not be encrypted via TLS automatically.
Reveal Answer

Correct Answer: B

When you publish a port using the -p <host_port>:<container_port> syntax without specifying a host IP, Docker assumes 0.0.0.0, meaning it will listen on all network interfaces. Furthermore, Docker manipulates iptables rules directly, often bypassing local host firewalls like ufw. If your cloud provider relies on the OS firewall rather than an external security group, this exposes your container directly to the public internet. To bind it safely for local use only, you must explicitly use -p 127.0.0.1:6379:6379.

Discussion

0

Join the discussion

Loading comments...

© 2026 Jatin Jain Saraf (JJS). All rights reserved.