
Docker Core Networking
- 9 installs
- 9 repo stars
- Updated July 8, 2026
- openaec-foundation/docker-claude-skill-package
Helps with devops & ci/cd tasks.
About
docker-core-networking is a Claude Code skill for devops & ci/cd. It helps solo builders move faster with AI-assisted development.
- docker-core-networking
- DevOps & CI/CD
- AI-coding skill
Docker Core Networking by the numbers
- 9 all-time installs (skills.sh)
- +1 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #1,020 of 1,435 DevOps & CI/CD skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/openaec-foundation/docker-claude-skill-package --skill docker-core-networkingAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 9 |
|---|---|
| repo stars | ★ 9 |
| Last updated | July 8, 2026 |
| Repository | openaec-foundation/docker-claude-skill-package ↗ |
What it does
Helps with devops & ci/cd tasks.
Files
docker-core-networking
Quick Reference
Network Drivers
| Driver | Isolation | Multi-Host | Use Case |
|---|---|---|---|
| bridge | Container-level | No | Default single-host container communication |
| host | None (shares host) | No | Performance-critical apps needing direct host network |
| overlay | Container-level | Yes (Swarm) | Cross-host service communication |
| macvlan | Container-level | No | Containers appear as physical LAN devices |
| ipvlan | Container-level | No | VLAN integration without MAC-per-container |
| none | Complete | No | Fully isolated containers with no networking |
Default Bridge vs User-Defined Bridge
| Feature | Default Bridge | User-Defined Bridge |
|---|---|---|
| DNS resolution | IP only (no name resolution) | Automatic by container name |
| Isolation | ALL containers join by default | Only explicitly connected containers |
| Live connect/disconnect | Requires container recreation | On-the-fly via docker network connect |
| Configuration | Shared, daemon restart needed | Per-network, independent |
| Recommended | NEVER for production | ALWAYS use this |
Port Mapping Syntax
| Syntax | Meaning |
|---|---|
-p 8080:80 | Host port 8080 to container port 80 |
-p 127.0.0.1:8080:80 | Bind to localhost only |
-p 80:8080/tcp | TCP only (default) |
-p 80:8080/udp | UDP only |
-p 80:8080/tcp -p 80:8080/udp | Both TCP and UDP |
-p 8000-8010:8000-8010 | Port range mapping |
-P | All EXPOSE ports to random host ports |
CLI Command Reference
| Command | Purpose |
|---|---|
docker network create | Create a network |
docker network connect | Connect running container to network |
docker network disconnect | Disconnect container from network |
docker network ls | List networks |
docker network inspect | Show network details |
docker network rm | Remove network |
docker network prune | Remove all unused networks |
Critical Warnings
ALWAYS use user-defined bridge networks instead of the default bridge. The default bridge lacks DNS resolution, proper isolation, and per-network configuration.
NEVER use --link for container communication -- it is legacy and deprecated. Use user-defined networks with DNS-based service discovery instead.
NEVER publish ports with -p 0.0.0.0:PORT:PORT on production hosts unless external access is intended. Use -p 127.0.0.1:PORT:PORT to restrict to localhost.
ALWAYS use --internal flag when creating networks that should have no external (internet) access. This prevents accidental data exfiltration.
NEVER rely on container IP addresses for communication -- IPs change on container restart. ALWAYS use container names or network aliases for DNS-based discovery.
---
Network Driver Decision Tree
Need container networking?
├── No → use --network none
└── Yes
├── Need host-level performance (no NAT overhead)?
│ └── Yes → use --network host
├── Need multi-host communication (Swarm)?
│ └── Yes → use overlay driver
│ ├── Need standalone container access? → --attachable
│ └── Need encryption? → --opt encrypted
├── Need container to appear as physical device on LAN?
│ ├── Yes, one MAC per container → use macvlan
│ └── Yes, shared MAC (VLAN) → use ipvlan
└── Single-host container communication
└── ALWAYS use user-defined bridge
└── docker network create mynet---
DNS Resolution
How DNS Works in User-Defined Networks
Docker runs an embedded DNS server at 127.0.0.11 for all user-defined networks. Containers resolve each other by:
1. Container name -- The --name value becomes a DNS hostname 2. Network alias -- Additional DNS names via --network-alias 3. Service name -- In Compose, the service key is the DNS name
Container A (name: web) Container B (name: api)
| |
|--- DNS query: "api" ---------> |
| 127.0.0.11 |
|<-- Response: 172.20.0.3 -------|
| |
|--- HTTP GET api:8080 --------->| (resolved via DNS)DNS Configuration
# Custom DNS server for external resolution
docker run --dns 8.8.8.8 nginx
# Custom search domain
docker run --dns-search example.com nginx
# Custom DNS options
docker run --dns-option ndots:2 nginxKey DNS Rules
- Default bridge: Containers inherit host
/etc/resolv.conf-- NO container name resolution - User-defined networks: Docker DNS at
127.0.0.11-- FULL container name resolution - Multiple networks: A container resolves names ONLY for containers on the same network
- External DNS: Queries not matching container names forward to configured upstream DNS
---
Network Creation and IPAM
Basic Network Creation
# Simple user-defined bridge
docker network create mynet
# Bridge with custom subnet
docker network create --driver bridge \
--subnet=172.28.0.0/16 \
--gateway=172.28.0.1 \
mynet
# Bridge with custom IP allocation range
docker network create --driver bridge \
--subnet=172.28.0.0/16 \
--ip-range=172.28.5.0/24 \
--gateway=172.28.5.254 \
mynet
# IPv6-enabled network
docker network create --ipv6 --subnet 2001:db8::/64 v6net
# Internal network (no external access)
docker network create --internal isolatedIPAM Configuration
| Option | Purpose | Example |
|---|---|---|
--subnet | Network address range | --subnet=172.28.0.0/16 |
--gateway | Default gateway address | --gateway=172.28.0.1 |
--ip-range | Allocatable IP range within subnet | --ip-range=172.28.5.0/24 |
--ipv6 | Enable IPv6 | --ipv6 |
--aux-address | Reserve addresses | --aux-address="switch=172.28.0.2" |
Static IP Assignment
# Assign static IP to container (requires user-defined network with subnet)
docker network connect --ip 172.28.5.10 mynet myapp
docker run --network mynet --ip 172.28.5.10 nginx---
Container-to-Container Communication
Same Network (Recommended)
# Create network
docker network create app-net
# Run containers on the same network
docker run -d --name db --network app-net postgres:16
docker run -d --name api --network app-net \
-e DATABASE_URL=postgresql://db:5432/mydb myapp
# api can reach db by name "db" via DNSMultiple Networks for Isolation
# Frontend network (web + api)
docker network create frontend
# Backend network (api + db)
docker network create backend
# Web server -- only frontend
docker run -d --name web --network frontend -p 80:80 nginx
# API server -- both networks (bridge between frontend and backend)
docker run -d --name api --network frontend myapi
docker network connect backend api
# Database -- only backend (unreachable from web)
docker run -d --name db --network backend postgres:16Network Aliases
# Multiple containers behind one DNS name (client-side load balancing)
docker run -d --network mynet --network-alias search elasticsearch:8
docker run -d --network mynet --network-alias search elasticsearch:8
# Both containers resolve via "search" -- Docker round-robins responsesPort Exposure Rules
- Containers on the SAME user-defined network expose ALL ports to each other automatically
-pflag is ONLY needed for access from outside the Docker network (host or external)- The
EXPOSEinstruction in Dockerfile is documentation only -- it does NOT publish ports
---
Docker Compose Networking
Default Behavior
Compose automatically creates a network named {project}_default and connects all services:
# All services can reach each other by service name
services:
web:
image: nginx
ports:
- "80:80" # Published to host
api:
image: myapi
# Reaches db via hostname "db" automatically
db:
image: postgres:16
# No ports published -- only accessible within Compose networkCustom Networks in Compose
services:
web:
image: nginx
networks:
- frontend
api:
image: myapi
networks:
- frontend
- backend
db:
image: postgres:16
networks:
- backend
networks:
frontend:
driver: bridge
backend:
driver: bridge
internal: true # No external accessExternal Networks
# Use pre-existing network (MUST exist before docker compose up)
networks:
existing-net:
external: true---
Network Inspection and Debugging
Inspect Commands
# List all networks
docker network ls
docker network ls --filter driver=bridge
# Inspect network details (containers, IPAM, options)
docker network inspect mynet
# Get containers on a network
docker network inspect --format='{{range .Containers}}{{.Name}} {{end}}' mynet
# Get container IP address
docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' myappDebug Connectivity
# Test DNS resolution from inside container
docker exec myapp nslookup other-container
# Test connectivity
docker exec myapp ping -c 2 other-container
# Check container's DNS config
docker exec myapp cat /etc/resolv.conf
# Check which networks a container belongs to
docker inspect --format='{{range $k, $v := .NetworkSettings.Networks}}{{$k}} {{end}}' myapp---
Reference Links
- references/drivers.md -- All network driver details, options, and use cases
- references/examples.md -- Network creation, multi-container networking, isolation patterns
- references/anti-patterns.md -- Common networking mistakes and how to avoid them
Official Sources
- https://docs.docker.com/engine/network/
- https://docs.docker.com/engine/network/drivers/bridge/
- https://docs.docker.com/engine/network/drivers/overlay/
- https://docs.docker.com/engine/network/drivers/host/
- https://docs.docker.com/engine/network/drivers/macvlan/
- https://docs.docker.com/engine/network/drivers/ipvlan/
- https://docs.docker.com/compose/how-tos/networking/
Networking Anti-Patterns
AP-1: Using the Default Bridge Network
Problem
# Containers on the default bridge -- NO DNS resolution
docker run -d --name db postgres:16
docker run -d --name api -e DB_HOST=db myapi
# api CANNOT resolve "db" by name -- connection failsWhy It Fails
The default bridge network does NOT provide automatic DNS resolution. Containers can only reach each other by IP address, which changes on every container restart.
Correct Approach
# ALWAYS create a user-defined bridge network
docker network create app-net
docker run -d --name db --network app-net postgres:16
docker run -d --name api --network app-net -e DB_HOST=db myapi
# api resolves "db" automatically via Docker's embedded DNS---
AP-2: Using --link for Container Communication
Problem
# Legacy --link flag -- deprecated and will be removed
docker run -d --name db postgres:16
docker run -d --link db:database myapiWhy It Fails
--link is a legacy feature that only works on the default bridge. It does NOT work with user-defined networks, does NOT support dynamic discovery, and will be removed in a future Docker release.
Correct Approach
docker network create app-net
docker run -d --name db --network app-net postgres:16
docker run -d --name api --network app-net myapi
# Use container name "db" as hostname -- works with DNS---
AP-3: Hardcoding Container IP Addresses
Problem
docker run -d --name db --network app-net postgres:16
# Get IP and hardcode it
DB_IP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' db)
docker run -d --name api --network app-net -e DB_HOST=$DB_IP myapiWhy It Fails
Container IP addresses are ephemeral. They change when containers restart, are recreated, or when the network is recreated. Hardcoded IPs break silently.
Correct Approach
# ALWAYS use container names or network aliases for discovery
docker run -d --name api --network app-net -e DB_HOST=db myapi
# "db" resolves dynamically via DNS -- survives container restarts---
AP-4: Publishing Ports on All Interfaces
Problem
# Exposes database to entire network and internet
docker run -d -p 5432:5432 postgres:16
# Equivalent to -p 0.0.0.0:5432:5432Why It Fails
Publishing on 0.0.0.0 makes the service accessible from any network interface, including public interfaces. Database ports, admin panels, and internal services become exposed to attackers.
Correct Approach
# Bind to localhost only for internal services
docker run -d -p 127.0.0.1:5432:5432 postgres:16
# For services that only need container-to-container access,
# do NOT publish ports at all -- use a shared network instead
docker network create backend
docker run -d --name db --network backend postgres:16
docker run -d --name api --network backend myapi
# api reaches db:5432 directly -- no host port exposure---
AP-5: Publishing Ports for Container-to-Container Communication
Problem
# Publishing ports when only containers need to communicate
docker run -d --name db --network app-net -p 5432:5432 postgres:16
docker run -d --name cache --network app-net -p 6379:6379 redis:7
docker run -d --name api --network app-net myapiWhy It Fails
Containers on the same user-defined network can reach ALL ports on other containers automatically. Publishing ports (-p) is ONLY needed for access from outside the Docker network. Unnecessary port publishing increases the attack surface.
Correct Approach
# No -p needed for inter-container communication
docker run -d --name db --network app-net postgres:16
docker run -d --name cache --network app-net redis:7
docker run -d --name api --network app-net -p 3000:3000 myapi
# Only api port 3000 is published -- the one that needs host/external access---
AP-6: All Containers on a Single Network
Problem
services:
web:
image: nginx
api:
image: myapi
db:
image: postgres:16
cache:
image: redis:7
monitoring:
image: prometheus
# All on the default Compose network -- every service can reach every other serviceWhy It Fails
No network segmentation means the web server can directly access the database, monitoring can reach application internals, and a compromise of any container gives network access to all others.
Correct Approach
services:
web:
networks: [frontend]
api:
networks: [frontend, backend]
db:
networks: [backend]
cache:
networks: [backend]
monitoring:
networks: [monitoring, backend]
networks:
frontend:
backend:
internal: true
monitoring:---
AP-7: Using Host Network Mode by Default
Problem
# Using host network for convenience, not performance
docker run --network host mywebapp
docker run --network host another-appWhy It Fails
Host networking removes ALL network isolation. Multiple containers compete for the same ports. A compromised container has full access to the host's network interfaces, listening sockets, and can sniff traffic.
Correct Approach
# Use host networking ONLY when NAT overhead is measurable and unacceptable
# For normal applications, use bridge networking with port mapping
docker network create app-net
docker run -d --name web --network app-net -p 80:80 mywebapp---
AP-8: Ignoring DNS Resolution Differences Between Networks
Problem
docker network create net-a
docker network create net-b
docker run -d --name db --network net-a postgres:16
docker run -d --name api --network net-b myapi
# api tries to resolve "db" -- FAILS because they are on different networksWhy It Fails
Docker DNS resolution is scoped to each network. A container can ONLY resolve names of containers on the same network. Containers on different networks are invisible to each other.
Correct Approach
# Option 1: Same network
docker run -d --name api --network net-a myapi
# Option 2: Connect api to both networks
docker run -d --name api --network net-b myapi
docker network connect net-a api
# Now api can resolve names on BOTH networks---
AP-9: Not Using --internal for Sensitive Backend Networks
Problem
# Backend network has internet access by default
docker network create backend
docker run -d --name db --network backend postgres:16
# A compromised db container can exfiltrate data to the internetWhy It Fails
By default, bridge networks provide outbound internet access via NAT. Backend services (databases, caches, queues) rarely need internet access. Leaving it enabled creates an exfiltration path.
Correct Approach
# Internal network -- no outbound internet access
docker network create --internal backend
docker run -d --name db --network backend postgres:16
# db can communicate with other containers on backend, but CANNOT reach the internet---
AP-10: Forgetting Overlay Network Port Requirements
Problem
# Swarm nodes cannot communicate -- overlay network broken
docker swarm init
docker network create -d overlay my-overlay
docker service create --network my-overlay --replicas 3 myapp
# Containers on different hosts cannot reach each otherWhy It Fails
Overlay networks require specific ports open between ALL Swarm nodes:
- 2377/tcp -- Swarm management
- 4789/udp -- VXLAN data
- 7946/tcp+udp -- Node discovery
If firewall rules block these ports, overlay networking fails silently or intermittently.
Correct Approach
# Verify required ports are open on ALL Swarm nodes
# On each node's firewall:
ufw allow 2377/tcp # Swarm management
ufw allow 4789/udp # VXLAN
ufw allow 7946/tcp # Node discovery
ufw allow 7946/udp # Node discovery
# Then create overlay network
docker network create -d overlay --attachable my-overlay---
AP-11: Relying on EXPOSE for Security
Problem
# Dockerfile
EXPOSE 80
# "Only port 80 is accessible" -- WRONG assumptionWhy It Fails
The EXPOSE instruction is documentation only. It does NOT restrict which ports are accessible. Containers on the same network can access ANY port on any other container, regardless of EXPOSE. The only things that control port access are:
- Network membership (which network the container is on)
-pflag (which ports are published to the host)--internalflag (blocks all external access)
Correct Approach
Use network segmentation and -p flags for access control. Treat EXPOSE as documentation for operators, not as a security mechanism.
---
AP-12: Not Cleaning Up Unused Networks
Problem
# Creating networks for temporary tasks and never removing them
docker network create test-1
docker network create test-2
docker network create experiment
# Months later: dozens of orphaned networks consuming address spaceWhy It Fails
Docker has a limited default address pool. Each unused network holds a subnet allocation. Eventually, new network creation fails with "could not find an available, non-overlapping IPv4 address pool."
Correct Approach
# Regular cleanup of unused networks
docker network prune -f
# Or with age filter
docker network prune -f --filter "until=24h"
# Check before pruning
docker network ls---
Official Sources
- https://docs.docker.com/engine/network/
- https://docs.docker.com/engine/network/drivers/bridge/
- https://docs.docker.com/engine/network/drivers/overlay/
Network Drivers Reference
Bridge Driver (Default)
Overview
The bridge driver creates an isolated network on a single Docker host. Containers on the same bridge can communicate; containers on different bridges cannot (unless connected to both).
Docker creates a default bridge network (bridge) at startup. ALWAYS create user-defined bridge networks instead of using the default.
User-Defined Bridge Creation
# Basic bridge
docker network create mynet
# Bridge with full IPAM configuration
docker network create --driver bridge \
--subnet=172.28.0.0/16 \
--ip-range=172.28.5.0/24 \
--gateway=172.28.5.254 \
mynet
# Bridge with custom options
docker network create --driver bridge \
-o com.docker.network.bridge.name=my-bridge0 \
-o com.docker.network.bridge.enable_icc=true \
-o com.docker.network.driver.mtu=1500 \
mynetBridge Driver Options (-o)
| Option | Default | Description |
|---|---|---|
com.docker.network.bridge.name | auto | Linux bridge interface name |
com.docker.network.bridge.enable_ip_masquerade | true | Enable NAT for outbound traffic |
com.docker.network.bridge.enable_icc | true | Inter-container connectivity on this bridge |
com.docker.network.bridge.host_binding_ipv4 | 0.0.0.0 | Default IP for port binding |
com.docker.network.driver.mtu | 0 (no limit) | Maximum Transmission Unit |
com.docker.network.container_iface_prefix | eth | Container interface prefix |
com.docker.network.bridge.inhibit_ipv4 | false | Skip IPv4 gateway assignment |
Default Bridge vs User-Defined Bridge -- Detailed Comparison
DNS Resolution
# Default bridge: NO DNS resolution -- must use IP or --link (deprecated)
docker run -d --name db postgres:16
docker run -it --rm alpine ping db
# ping: bad address 'db' <-- FAILS
# User-defined bridge: AUTOMATIC DNS resolution by container name
docker network create mynet
docker run -d --name db --network mynet postgres:16
docker run -it --rm --network mynet alpine ping db
# PING db (172.20.0.2): 56 data bytes <-- WORKSIsolation
- Default bridge: Every container without
--networkjoins the default bridge. ALL such containers can communicate with each other -- no isolation between unrelated applications. - User-defined bridge: Only containers explicitly connected can communicate. Different applications use different networks for isolation.
Live Connect/Disconnect
# User-defined networks support hot-plugging
docker network connect mynet running-container
docker network disconnect mynet running-container
# Default bridge requires stopping and recreating the containerConfiguration
- Default bridge: Configured via
daemon.json, requires daemon restart to change - User-defined bridge: Configured per-network at creation time, each network independent
Scalability
Bridge networks become unstable at 1000+ containers per network due to Linux kernel limitations. For large deployments, distribute containers across multiple networks.
---
Host Driver
Overview
The host driver removes network isolation entirely. The container shares the host's network namespace -- it uses the host's IP address and port space directly.
Usage
docker run --network host nginx
# Nginx binds to host port 80 directly -- no NAT, no port mapping neededCharacteristics
| Property | Value |
|---|---|
Port mapping (-p) | NOT needed and NOT supported |
| Performance | Best (no NAT overhead) |
| Isolation | None -- container sees all host interfaces |
| DNS | Uses host's DNS directly |
| Platform | Linux only (on Docker Desktop, "host" means the VM, not your machine) |
When to Use Host Networking
- Performance-critical applications where NAT overhead matters
- Applications that need to bind many ports dynamically (port mapping impractical)
- Network monitoring tools that need to see all host traffic
- Applications that must advertise their real host IP to external systems
When NOT to Use Host Networking
- NEVER use in production if isolation is a security requirement
- NEVER use when multiple containers need the same port
- NEVER use on Docker Desktop expecting to reach the physical host network
---
Overlay Driver
Overview
Overlay networks span multiple Docker hosts using Swarm mode. They use VXLAN encapsulation to create a virtual Layer 2 network across hosts.
Prerequisites
- Docker Swarm mode initialized (
docker swarm init) - Required ports open between hosts:
- 2377/tcp -- Swarm management
- 4789/udp -- VXLAN overlay traffic
- 7946/tcp+udp -- Node discovery and gossip
Creation
# Basic overlay (Swarm services only)
docker network create -d overlay my-overlay
# Attachable overlay (standalone containers + Swarm services)
docker network create -d overlay --attachable my-overlay
# Encrypted overlay (IPsec encryption on VXLAN)
docker network create -d overlay --opt encrypted --attachable secure-overlayOverlay Driver Options
| Option | Default | Description |
|---|---|---|
encrypted | false | Enable IPSEC encryption on VXLAN |
com.docker.network.driver.mtu | 1450 | VXLAN MTU (lower than bridge due to encapsulation) |
Characteristics
| Property | Value |
|---|---|
| Multi-host | Yes (Swarm required) |
| Encryption | Optional via --opt encrypted |
| Service discovery | Automatic via Swarm DNS |
| Load balancing | Built-in via Swarm routing mesh |
| Platform | Linux only for encryption |
Limitations
- Windows containers CANNOT use encrypted overlay networks
- Same 1000-container-per-host scalability limit applies
- Encryption adds performance overhead (IPsec)
--attachableis required for standalone containers (non-service)
---
Macvlan Driver
Overview
Macvlan assigns a unique MAC address to each container, making it appear as a physical device on the network. Containers get IP addresses from the physical network's DHCP server or static assignment.
Creation
# Macvlan with static subnet
docker network create -d macvlan \
--subnet=192.168.1.0/24 \
--gateway=192.168.1.1 \
-o parent=eth0 \
my-macvlan
# Macvlan with VLAN tagging (802.1Q trunk)
docker network create -d macvlan \
--subnet=192.168.50.0/24 \
--gateway=192.168.50.1 \
-o parent=eth0.50 \
my-macvlan-vlan50Macvlan Modes
| Mode | Description |
|---|---|
| bridge (default) | Containers can communicate with each other and external network |
| passthru | Single container directly attached to parent interface |
Macvlan Driver Options
| Option | Description |
|---|---|
parent | Host interface to attach to (REQUIRED) |
macvlan_mode | bridge (default) or passthru |
Characteristics
| Property | Value |
|---|---|
| MAC address | Unique per container |
| IP address | From physical network range |
| Host communication | NOT possible by default (requires macvlan on host interface too) |
| Promiscuous mode | Required on parent interface |
| Use case | Legacy apps that need LAN presence, IoT, network appliances |
Limitations
- Host cannot communicate with macvlan containers without additional configuration (create a macvlan sub-interface on the host)
- Requires promiscuous mode on the parent interface
- Many cloud providers and wireless interfaces block promiscuous mode
- Each container consumes a MAC address -- some switches limit MAC addresses per port
---
IPvlan Driver
Overview
IPvlan is similar to macvlan but ALL containers share the parent interface's MAC address. Each container gets its own IP address. This avoids the MAC-per-container overhead and works where promiscuous mode is blocked.
Creation
# IPvlan L2 mode (default)
docker network create -d ipvlan \
--subnet=192.168.1.0/24 \
--gateway=192.168.1.1 \
-o parent=eth0 \
my-ipvlan
# IPvlan L3 mode (routing, no bridge)
docker network create -d ipvlan \
--subnet=192.168.100.0/24 \
-o parent=eth0 \
-o ipvlan_mode=l3 \
my-ipvlan-l3IPvlan Modes
| Mode | Layer | Description |
|---|---|---|
| l2 (default) | Layer 2 | Behaves like macvlan but with shared MAC |
| l3 | Layer 3 | Pure routing mode -- no broadcast, no ARP |
| l3s | Layer 3 + source | L3 with source-based routing and iptables integration |
IPvlan vs Macvlan
| Feature | Macvlan | IPvlan |
|---|---|---|
| MAC address | Unique per container | Shared (parent's MAC) |
| Promiscuous mode | Required | NOT required |
| Cloud compatibility | Often blocked | Generally works |
| L3 routing | No | Yes (l3 mode) |
| Broadcast/multicast | Yes | No (in l3 mode) |
When to Choose IPvlan over Macvlan
- Cloud environments that block promiscuous mode
- Switches with MAC address table limits
- When L3 routing mode is needed
- Wireless interfaces (no promiscuous mode support)
---
None Driver
Overview
The none driver provides complete network isolation. The container has only a loopback interface -- no external connectivity.
Usage
docker run --network none alpine ip addr
# Only shows lo (127.0.0.1)When to Use
- Batch processing containers that need no network access
- Security-sensitive workloads that must be completely isolated
- Containers that communicate ONLY through volumes or shared memory
- Testing network failure scenarios
---
Subnet Allocation
Default Address Pools
Docker allocates subnets from built-in pools:
172.17.0.0/16through172.28.0.0/14192.168.0.0/16
Custom Address Pools
Configure in /etc/docker/daemon.json:
{
"default-address-pools": [
{ "base": "10.10.0.0/16", "size": 24 },
{ "base": "172.30.0.0/16", "size": 24 }
]
}Each new network gets a /24 (or configured size) from the pool automatically.
Gateway Priority
When a container connects to multiple networks, the default gateway is selected by gw-priority (highest value wins, default: 0):
docker run --network name=primary,gw-priority=1 --network secondary myimage
# Default gateway comes from "primary" network---
Official Sources
- https://docs.docker.com/engine/network/
- https://docs.docker.com/engine/network/drivers/bridge/
- https://docs.docker.com/engine/network/drivers/overlay/
- https://docs.docker.com/engine/network/drivers/host/
- https://docs.docker.com/engine/network/drivers/macvlan/
- https://docs.docker.com/engine/network/drivers/ipvlan/
- https://docs.docker.com/engine/network/drivers/none/
Networking Examples
1. Basic User-Defined Bridge Network
# Create network
docker network create app-net
# Run database
docker run -d \
--name postgres \
--network app-net \
-e POSTGRES_PASSWORD=secret \
postgres:16
# Run application -- connects to postgres by name
docker run -d \
--name api \
--network app-net \
-e DATABASE_URL=postgresql://postgres:secret@postgres:5432/mydb \
-p 3000:3000 \
myapi:latest
# api resolves "postgres" via Docker DNS automatically---
2. Multi-Tier Network Isolation
Separate frontend, backend, and database tiers. Only the API can reach both the web tier and the database tier.
# Create isolated networks
docker network create frontend
docker network create backend --internal # No internet access
# Web server -- frontend only, published to host
docker run -d --name web --network frontend -p 80:80 nginx
# API server -- connected to BOTH networks
docker run -d --name api --network frontend myapi
docker network connect backend api
# Database -- backend only (unreachable from web, no internet)
docker run -d --name db --network backend \
-e POSTGRES_PASSWORD=secret \
postgres:16
# Result:
# web -> api (via frontend network) OK
# api -> db (via backend network) OK
# web -> db (no shared network) BLOCKED
# db -> internet (--internal) BLOCKED---
3. Docker Compose Multi-Network Setup
services:
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
networks:
- frontend
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
api:
image: myapi:latest
networks:
- frontend
- backend
environment:
- DATABASE_URL=postgresql://user:pass@db:5432/mydb
- REDIS_URL=redis://cache:6379
db:
image: postgres:16
networks:
- backend
volumes:
- pgdata:/var/lib/postgresql/data
environment:
- POSTGRES_PASSWORD=pass
cache:
image: redis:7-alpine
networks:
- backend
networks:
frontend:
driver: bridge
backend:
driver: bridge
internal: true
volumes:
pgdata:---
4. Network Aliases for Service Discovery
Multiple containers behind a single DNS name for client-side round-robin:
docker network create search-net
# Launch multiple Elasticsearch instances with same alias
docker run -d --name es1 --network search-net --network-alias search elasticsearch:8
docker run -d --name es2 --network search-net --network-alias search elasticsearch:8
docker run -d --name es3 --network search-net --network-alias search elasticsearch:8
# Client resolves "search" -- Docker rotates IP responses
docker run --rm --network search-net alpine nslookup search
# Returns all three IPs in round-robin orderIn Compose:
services:
worker:
image: myworker
deploy:
replicas: 3
networks:
app-net:
aliases:
- workers---
5. Static IP Assignment
# Network MUST have a subnet defined for static IPs
docker network create --subnet=172.28.0.0/16 static-net
# Assign specific IPs
docker run -d --name dns-server \
--network static-net \
--ip 172.28.0.53 \
coredns/coredns
docker run -d --name gateway \
--network static-net \
--ip 172.28.0.1 \
mygateway---
6. Container Sharing Network Namespace
Two containers share the same network namespace -- they communicate via localhost:
# Redis binds to localhost only
docker run -d --name redis redis:7 --bind 127.0.0.1
# Second container shares redis's network stack
docker run --rm -it --network container:redis redis:7 redis-cli -h 127.0.0.1
# Connects successfully via shared loopback---
7. Host Network for Performance
# No port mapping needed -- binds directly to host ports
docker run -d --name perf-app --network host myapp
# Application at host-ip:8080 directly
# No NAT overhead, maximum throughput---
8. Internal Network (No External Access)
# Create network with no internet access
docker network create --internal secure-net
# Containers can communicate with each other but NOT the internet
docker run -d --name app1 --network secure-net alpine sleep 3600
docker run -d --name app2 --network secure-net alpine sleep 3600
# app1 can reach app2
docker exec app1 ping -c 1 app2 # WORKS
# app1 cannot reach the internet
docker exec app1 ping -c 1 8.8.8.8 # FAILS (no route)---
9. Macvlan -- Container on Physical LAN
# Create macvlan network attached to physical interface
docker network create -d macvlan \
--subnet=192.168.1.0/24 \
--gateway=192.168.1.1 \
-o parent=eth0 \
lan-net
# Container appears as device 192.168.1.100 on the physical network
docker run -d --name iot-bridge \
--network lan-net \
--ip 192.168.1.100 \
my-iot-app
# Other devices on 192.168.1.0/24 can reach this container directly---
10. Overlay Network (Multi-Host Swarm)
# On manager node
docker swarm init
# Create overlay network
docker network create -d overlay --attachable my-overlay
# Deploy service across multiple hosts
docker service create --name web \
--network my-overlay \
--replicas 3 \
-p 80:80 \
nginx
# Standalone container can also join (because --attachable)
docker run -d --name debug --network my-overlay alpine sleep 3600---
11. Encrypted Overlay Network
# Create encrypted overlay (IPsec between hosts)
docker network create -d overlay \
--opt encrypted \
--attachable \
secure-overlay
# All VXLAN traffic between hosts is encrypted
docker service create --name secure-web \
--network secure-overlay \
nginx---
12. Custom IPAM Configuration
# Network with specific subnet, allocation range, and gateway
docker network create \
--driver bridge \
--subnet=10.100.0.0/16 \
--ip-range=10.100.1.0/24 \
--gateway=10.100.0.1 \
--aux-address="reserved1=10.100.1.1" \
custom-net
# Containers get IPs from 10.100.1.0/24 range
# 10.100.1.1 is reserved and won't be assigned---
13. IPv6 Networking
# Dual-stack network (IPv4 + IPv6)
docker network create --ipv6 \
--subnet=172.28.0.0/16 \
--subnet=2001:db8::/64 \
dual-stack-net
# IPv6-only network
docker network create --ipv4=false --ipv6 \
--subnet=2001:db8:1::/64 \
v6only-net---
14. Connecting a Running Container to Additional Networks
# Container starts on one network
docker run -d --name multi-net-app --network frontend myapp
# Hot-plug additional network
docker network connect backend multi-net-app
# Optionally with static IP and alias
docker network connect --ip 172.28.5.10 --alias myservice backend multi-net-app
# Disconnect when no longer needed
docker network disconnect frontend multi-net-app---
15. Network Debugging Session
# 1. Check which networks a container belongs to
docker inspect --format='{{range $k, $v := .NetworkSettings.Networks}}{{$k}}: {{$v.IPAddress}}{{"\n"}}{{end}}' myapp
# 2. Check DNS resolution from inside the container
docker exec myapp nslookup target-container
docker exec myapp cat /etc/resolv.conf
# 3. Test connectivity
docker exec myapp ping -c 2 target-container
docker exec myapp wget -qO- http://target-container:8080/health
# 4. Check which containers are on a network
docker network inspect mynet --format='{{range .Containers}}{{.Name}} ({{.IPv4Address}}){{"\n"}}{{end}}'
# 5. Check port bindings
docker port myapp
# 6. Check if ports are exposed to host
docker inspect --format='{{range $p, $conf := .NetworkSettings.Ports}}{{$p}} -> {{if $conf}}{{(index $conf 0).HostPort}}{{else}}not published{{end}}{{"\n"}}{{end}}' myapp---
16. Localhost Port Binding (Security)
# INSECURE: Binds to all interfaces (0.0.0.0)
docker run -p 5432:5432 postgres:16
# SECURE: Binds to localhost only
docker run -p 127.0.0.1:5432:5432 postgres:16
# Use specific interface IP
docker run -p 10.0.0.5:8080:80 nginx---
Official Sources
- https://docs.docker.com/engine/network/
- https://docs.docker.com/engine/network/drivers/bridge/
- https://docs.docker.com/engine/network/drivers/overlay/
- https://docs.docker.com/engine/network/drivers/macvlan/
- https://docs.docker.com/compose/how-tos/networking/