env.dev

Docker Networking: Bridge, Host, Overlay & Compose

Docker networking deep dive: bridge, host, overlay, macvlan drivers, port mapping, embedded DNS at 127.0.0.11, Compose isolated networks, and debugging.

Last updated:

Docker networking controls how containers communicate with each other, the host, and external networks through five pluggable drivers — bridge, host, overlay, macvlan, and none. Pick the wrong driver and you get DNS that silently returns the wrong container, port conflicts with the corporate VPN, or 30% throughput loss on hot paths. The Compose default network model has been stable since Compose v2 in 2021, but Docker Engine 25 (2024) shipped IPv6 by default on user-defined bridges and tightened the embedded DNS server at 127.0.0.11. For day-to-day commands, keep the Docker cheat sheet open in another tab.

What Are the Docker Network Drivers?

Docker uses pluggable network drivers. The default is bridge, which creates an isolated virtual network on the host. Each driver has different isolation, performance, and discoverability characteristics.

DriverScopeDNSUse Case
bridgeSingle hostUser-defined onlyDefault — isolated containers on one host
hostSingle hostHost's DNSNo network isolation — max performance
overlayMulti-hostYesSwarm services across multiple nodes
noneN/ANoCompletely isolated — no networking
macvlanSingle hostNoAssign a MAC address — appear as physical device

host networking eliminates the per-packet NAT overhead Docker imposes on bridge — measured around a 5–10% throughput gain on 10 Gbps interfaces and roughly 30 µs latency reduction on small RPCs. The trade-off is zero network isolation: the container sees the host's interfaces directly, and a port conflict crashes the container.

How Does Bridge Networking Work?

The default bridge network (docker0) provides basic connectivity but no automatic DNS resolution between containers. Always create a user-defined bridge network instead — it provides DNS, better isolation, and the ability to connect/disconnect containers at runtime.

Bridge network basics
# Create a user-defined bridge network
docker network create app-network

# Run containers on the network
docker run -d --name api --network app-network node:22-slim
docker run -d --name db --network app-network postgres:17

# The 'api' container can now reach 'db' by name
# From inside 'api': ping db → resolves to the container's IP

# Inspect the network (see connected containers, subnet, gateway)
docker network inspect app-network

# Disconnect a container from the network
docker network disconnect app-network api

# Remove the network (must disconnect all containers first)
docker network rm app-network

Default vs user-defined bridge: The default docker0 bridge requires --link for container name resolution (deprecated). User-defined bridges have automatic DNS, configurable subnets, and better isolation between groups of containers.

How Does Port Mapping Work?

Port mapping publishes container ports to the host using -p. Without it, container ports are only accessible from within the Docker network. This is also how the Dev Containers spec exposes a service running inside a workspace container to the editor on the host.

Port mapping options
# Map host port 8080 → container port 3000
docker run -p 8080:3000 myapp

# Map to a specific host interface (localhost only)
docker run -p 127.0.0.1:8080:3000 myapp

# Map a range of ports
docker run -p 8000-8010:8000-8010 myapp

# Let Docker pick a random host port
docker run -p 3000 myapp
# Check assigned port: docker port <container_id>

# UDP port mapping
docker run -p 5353:53/udp dns-server

# Multiple port mappings
docker run -p 80:80 -p 443:443 nginx

How Does DNS Resolution Work in Docker?

On user-defined networks, Docker runs an embedded DNS server at 127.0.0.11. Containers can resolve each other by container name or network alias. This is the foundation of service discovery in Docker.

DNS and service discovery
# Container name = DNS name (on user-defined networks)
docker run -d --name redis --network app-network redis:7

# From another container on the same network:
# redis:6379 resolves to the container's IP

# Network aliases — multiple DNS names for one container
docker run -d --name redis-primary \
  --network app-network \
  --network-alias redis \
  --network-alias cache \
  redis:7

# Both 'redis' and 'cache' resolve to this container

# Debug DNS resolution
docker run --rm --network app-network alpine nslookup redis

How do containers connect to the host?

The classic mistake: curl http://127.0.0.1:5432 from inside a container hits the container's loopback, not the host's. Reaching a service on the host machine depends on the platform.

  • Docker Desktop (Mac, Windows): use the magic hostname host.docker.internal. Docker resolves it to the host's IP from inside the VM. Works out of the box.
  • Linux: there is no built-in host.docker.internal. Add it explicitly with --add-host=host.docker.internal:host-gateway (Docker 20.10+, Aug 2020), or fall back to the bridge gateway IP 172.17.0.1.
  • Network namespace shortcut: --network host bypasses the question entirely — the container is already on the host's stack.
Reaching the host from a container
# Mac/Windows — already works
docker run --rm alpine ping -c 1 host.docker.internal

# Linux — opt in to the same hostname
docker run --rm \
  --add-host=host.docker.internal:host-gateway \
  alpine ping -c 1 host.docker.internal

# Linux fallback — bridge gateway IP
docker run --rm alpine sh -c 'apk add --no-cache curl && curl http://172.17.0.1:5432'

How Does Docker Compose Networking Work?

Docker Compose automatically creates a network for each project. All services in a compose.yaml can reach each other by service name. You can also define custom networks to isolate groups of services.

compose.yaml with multiple networks
services:
  api:
    image: node:22-slim
    ports:
      - "3000:3000"
    networks:
      - frontend
      - backend
    depends_on:
      - db
      - redis

  web:
    image: nginx:latest
    ports:
      - "80:80"
    networks:
      - frontend
    # 'web' can reach 'api' but NOT 'db' or 'redis'

  db:
    image: postgres:17
    environment:
      POSTGRES_PASSWORD: secret
    networks:
      - backend
    volumes:
      - pgdata:/var/lib/postgresql/data

  redis:
    image: redis:7
    networks:
      - backend

networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge
    internal: true  # No external access — db/redis isolated

volumes:
  pgdata:

How Does Multi-Host Networking Work?

Overlay networks span multiple Docker hosts, enabling containers on different machines to communicate as if on the same LAN. This is used in Docker Swarm and can be attached to standalone containers with the --attachable flag. Most teams running Swarm in 2026 are quietly migrating to Kubernetes — see the Docker vs Kubernetes comparison and the Kubernetes cheat sheet for the equivalent CNI-driven model.

Overlay network for multi-host communication
# Initialize Swarm (required for overlay networks)
docker swarm init

# Create an attachable overlay network
docker network create -d overlay --attachable multi-host-net

# Deploy a service across multiple nodes
docker service create \
  --name api \
  --network multi-host-net \
  --replicas 3 \
  myapp:latest

# Standalone containers can also join (with --attachable)
docker run -d --name debug --network multi-host-net alpine sleep 3600

# Inspect overlay network
docker network inspect multi-host-net

What are the common Docker networking pitfalls?

  • Default subnet collides with corporate VPN. Docker's default bridge sits at 172.17.0.0/16, which routinely overlaps with VPN or data-centre ranges. Pick a quiet block when you create the network: docker network create --subnet 10.42.0.0/16 app-net.
  • Overlay MTU is 1450, not 1500. VXLAN encapsulation eats 50 bytes. If you see TLS handshakes that succeed but large responses hang, set --opt com.docker.network.driver.mtu=1450 on the overlay (or lower if your underlay also tunnels).
  • exposeports in Compose. expose is documentation-only metadata for inter-container access; it does not publish a port to the host. Only ports opens a host-side mapping.
  • iptables rules persist after Docker restarts. Docker writes rules into the DOCKER chain on daemon start; manual edits to those chains are wiped, and stale rules from earlier containers can survive crashes. Reset with systemctl restart docker rather than hand-editing.
  • 127.0.0.1 inside the container is not the host. It's the container's own loopback. Use host.docker.internal (Mac/Windows or Linux with --add-host) to talk to a service on the host.

How Do You Debug Docker Networking Issues?

When containers cannot reach each other, use these commands to diagnose the issue.

Debugging commands
# List all networks
docker network ls

# Inspect a network (shows connected containers, IPs, subnet)
docker network inspect app-network

# Check which networks a container is connected to
docker inspect --format='{{json .NetworkSettings.Networks}}' my-container

# Test connectivity from inside a container
docker exec -it api ping db
docker exec -it api curl http://api:3000/health

# Run a temporary debug container on the network
docker run --rm -it --network app-network nicolaka/netshoot
# Inside: dig redis, nslookup db, curl api:3000, tcpdump, etc.

# Check published ports
docker port my-container

# View iptables rules Docker created (Linux)
sudo iptables -t nat -L -n | grep DOCKER

References

Key Takeaways

  • • Always use user-defined bridge networks — they provide automatic DNS resolution between containers
  • • The default docker0 bridge lacks DNS and should be avoided
  • • Port mapping (-p host:container) is required to expose container ports to the host
  • • Docker Compose creates a project network automatically — services resolve by service name
  • • Use internal: true networks to isolate backend services from external access
  • • Overlay networks enable cross-host communication in Swarm deployments
  • • Use docker network inspect and netshoot for debugging connectivity

Generate a production-ready Dockerfile for any stack with the Dockerfile Generator tool.

Was this helpful?

Read next

Docker Compose Environment Variables: The Complete Guide

Use environment variables in Docker Compose: .env auto-load, env_file, --env-file, multi-environment patterns, substitution, and secrets.

Continue →

Frequently Asked Questions

What is the default Docker network?

Docker creates a default bridge network. Containers on this network can communicate by IP but not by name. User-defined bridge networks add automatic DNS resolution between containers by container name.

When should I use host networking?

Use host networking (--network host) when you need maximum network performance or need to bind to many ports. The container shares the host network stack directly. Not available on Docker Desktop for Mac/Windows.

How does Docker Compose networking work?

Compose automatically creates a network for each project. Services can reach each other by service name. Use networks: in compose.yml to create multiple isolated networks or connect to external networks.

How do I connect from a container to a service on the host?

On Docker Desktop (Mac/Windows): use `host.docker.internal` as the hostname — it resolves to the host. On Linux: add `--add-host=host.docker.internal:host-gateway` (Docker 20.10+) or use the bridge gateway IP `172.17.0.1`.

Stay up to date

Get notified about new guides, tools, and cheatsheets.