## Intro

Docker networking decides how your services discover each other and how customers reach them. In this guide, you will:

- Understand bridge, host, and overlay networks

- Publish ports safely so traffic reaches the right container

- Use DNS between containers so services talk by name

- Define networks in Docker Compose for repeatable setups

- Troubleshoot broken connectivity fast

This is written for busy operators who want practical steps and copy-paste examples that work on real machines.

## Workflow Overview

Use this sequence to choose and implement the right network mode:

- Clarify scope

- Single host : Start with a user-defined bridge network. It isolates containers and provides built-in DNS between them.

- Need bare-metal-like performance on Linux : Consider host networking. It removes NAT but exposes container ports directly on the host network stack.

- Multi-host : Use an overlay network with Swarm to connect services across nodes.

- Create the network

- Bridge (user-defined) : docker network create app-net

- Host : no network create step; you attach with --network host

- Overlay : docker swarm init ; then docker network create -d overlay --attachable app-ol

- Run services on the chosen network

- Bridge : attach with --network app-net or via Compose networks

- Host : run with --network host ; ensure the app binds to 0.0.0.0

- Overlay : use services ( docker service ... ) or a stack file ( docker stack deploy )

- Publish only what you need

- Bridge/overlay containers : publish host ports with -p or Compose ports

- Host network : skip -p ; the container uses the host stack directly

- Verify and document

- From one container: resolve peer by name ( curl http://api:8080 )

- From host: hit published ports ( curl http://localhost:8080 )

- Note chosen ports and networks for reuse

## Bridge networking in practice (local default)

A user-defined bridge network isolates traffic and enables service name DNS by default. Prefer this to the legacy default bridge called bridge .

Create a network:

docker network create app-net 
 Run a database and API on the same network:

# PostgreSQL
docker run -d \
 --name db \
 --network app-net \
 -e POSTGRES_PASSWORD=secret \
 postgres:16

# Simple API listening on 8080 and using DATABASE_HOST=db
docker run -d \
 --name api \
 --network app-net \
 -e DATABASE_HOST=db \
 -p 8080:8080 \
 myorg/api:latest 
 What you get:

- Container-to-container DNS: db resolves inside the network; the API can connect to db:5432

- Host access: customers or your tests hit http://localhost:8080 thanks to -p 8080:8080

- Isolation: other networks cannot reach these containers by name

Verify DNS from inside a container:

docker exec -it api sh -lc 'ping -c1 db || true'; \
 docker exec -it api sh -lc 'getent hosts db || true' 

### Minimal API Dockerfile example

 # Dockerfile
FROM python:3.11-slim
WORKDIR /app
COPY app.py .
RUN pip install --no-cache-dir flask psycopg2-binary
ENV PORT=8080
CMD ["python", "app.py\] 
 Example app.py (very small):

from flask import Flask
import os
app = Flask(__name__)

@app.get(\/\)
def hello():
 return {"ok": True, "port": os.getenv("PORT", "8080\)}

app.run(host="0.0.0.0", port=int(os.getenv("PORT", "8080\))) 
 Build and run:

docker build -t myorg/api:latest . 

## Port publishing basics

- Syntax: -p hostPort:containerPort[/protocol]

- Publishing exposes a container port on the host. Inside the Docker network, other containers still connect to the container port, not the host port.

- On host network: do not use -p . Your app must bind to 0.0.0.0 and the port must be free on the host.

- You can run multiple containers that listen on the same container port if you publish different host ports (e.g., -p 8081:8080 and -p 8082:8080 ).

 Check published ports:

docker ps --format 'table {{.Names}}\t{{.Ports}}' 

## DNS between containers

 On user-defined bridge and overlay networks, Docker provides an embedded DNS resolver. Containers resolve each other by name:

- Single host : use the container name on the same user-defined bridge (e.g., http://api:8080 )

- Compose : use the service name (e.g., http://web:80 )

Inside a container, /etc/resolv.conf typically points to 127.0.0.11 , the Docker DNS server for the network.

## Docker Compose networks (recommended for app stacks)

Docker Compose auto-creates a dedicated network for your project and gives each service a DNS name equal to its service key. This is the most repeatable local pattern.

compose.yaml :

version: "3.9"
services:
 db:
 image: postgres:16
 environment:
 POSTGRES_PASSWORD: secret
 networks: [app-net]

 api:
 build: .
 environment:
 DATABASE_HOST: db
 ports:
 - "8080:8080"
 depends_on: [db]
 networks: [app-net]

 web:
 image: nginx:alpine
 ports:
 - "8081:80"
 networks: [app-net]

networks:
 app-net:
 driver: bridge 
 Usage:

docker compose up -d
# Test service-to-service
docker compose exec web sh -lc 'wget -qO- http://api:8080 || curl -s http://api:8080'
# Test from host
curl -s http://localhost:8081 

## Host networking (Linux-only in most cases)

 Host networking puts the container on the host network stack. Pros: no NAT, lower overhead, simpler inbound traffic. Cons: less isolation, port conflicts, and it is generally Linux-only.

Example:

# Ensure your app binds to 0.0.0.0:8080
docker run -d --name fastapi --network host myorg/api:latest
# Now the app is reachable on the host at :8080 without -p 
 Consider host mode only when you truly need it and understand the security and operational trade-offs.

## Overlay networking (multi-host with Swarm)

Overlay networks connect services across multiple Docker hosts. You need Swarm enabled.

Initialize Swarm and create an attachable overlay:

docker swarm init
# Create an overlay network that both services and standalone containers can use
docker network create -d overlay --attachable app-ol 
 Deploy services with published ports:

# API service
docker service create \
 --name api \
 --network app-ol \
 -p 8080:8080 \
 myorg/api:latest

# Web service
docker service create \
 --name web \
 --network app-ol \
 -p 80:80 \
 nginx:alpine 
 Or use a stack file ( stack.yaml ):

version: "3.9"
services:
 api:
 image: myorg/api:latest
 ports: ["8080:8080\)
 networks: [app-ol]
 web:
 image: nginx:alpine
 ports: ["80:80\)
 networks: [app-ol]
networks:
 app-ol:
 driver: overlay 
 Deploy the stack:

docker stack deploy -c stack.yaml mystack 
 Verify:

docker service ls
curl -s http://<any-node-ip>:80 

## Troubleshooting broken connectivity

 Use this checklist to isolate common issues fast.

- Are containers on the same network?

docker network inspect app-net --format '{{json .Containers}}' | jq '.' 
 If the peer is not listed, connect it:

docker network connect app-net api 

- Is the port actually published on the host?

 docker ps --format 'table {{.Names}}\t{{.Ports}}'
# Look for 0.0.0.0:8080->8080/tcp (or your chosen host IP/port) 
 If missing, add -p or Compose ports .

- Does the app bind to 0.0.0.0 inside the container?

# Replace with a tool available in your image
docker exec -it api sh -lc 'ss -tln || netstat -tln' 
 If it binds only to 127.0.0.1 , update the app to listen on 0.0.0.0 .

- Can containers resolve each other by name?

docker exec -it api sh -lc 'getent hosts db || ping -c1 db || nslookup db || true' 
 If resolution fails, ensure you use a user-defined bridge or overlay and that both containers join the same network.

- Host firewall blocking traffic?

- Ensure your host allows inbound on the published ports (e.g., 80, 8080). Adjust your firewall rules if needed.

- Port conflicts on host?

# On Linux
ss -tulpn | grep :8080 || netstat -tulpn | grep :8080 
 If a process already uses the host port, pick a different one or stop the conflicting service.

- Overlay-specific checks

docker info | grep -i swarm
# Should show: Swarm: active

docker service ps api 
 If tasks are failing, check logs and node availability. Ensure all services attach to the same overlay.

If you used the legacy default bridge network, switch to a user-defined bridge for better DNS and isolation:

docker network create app-net
# Recreate containers with --network app-net 
 When in doubt, remove and recreate containers with explicit networks and ports:

docker rm -f api db web || true
docker network rm app-net || true 
 Then re-run known-good commands or Compose.

## Local Pilot Plan

Keep the first pilot small, measurable, and easy to inspect locally.

Goal

- A 3-service stack (db, api, web) on a user-defined bridge with working DNS and published ports.

- Success metric: curl from host returns expected JSON and Nginx welcome page within 200 ms on localhost.

Steps

- Build the API image and create the network

docker build -t myorg/api:latest .
docker network create app-net 

- Start services

 docker run -d --name db --network app-net -e POSTGRES_PASSWORD=secret postgres:16
docker run -d --name api --network app-net -e DATABASE_HOST=db -p 8080:8080 myorg/api:latest
docker run -d --name web --network app-net -p 8081:80 nginx:alpine 

- Verify

 # Container-to-container DNS
docker exec web sh -lc 'wget -qO- http://api:8080 || curl -s http://api:8080'
# Host access
curl -sS http://localhost:8080
curl -sS http://localhost:8081 

- Document ports and network name for reuse across future services.

 Optional next pilot (overlay)

- Enable Swarm and repeat with an overlay network across two hosts or two VMs.

## Conclusion

- Use a user-defined bridge for most single-host applications. You get isolation, predictable DNS between containers, and simple port publishing.

- Consider host networking only on Linux and only when you need lower overhead and understand the trade-offs.

- Use overlay networks with Swarm for multi-host setups.

- Publish only the ports you need, verify from both the host and between containers, and keep a short troubleshooting checklist at hand.

- Start with a small, measurable local pilot, then scale the same pattern to more services and, when needed, multiple hosts.