Intro
Understanding Docker bridge networking at an advanced level means moving beyond docker run -p 8080:80 and into the actual mechanics: how containers get IPs, how DNS resolution works, what iptables rules are created, and how to diagnose a broken connection. This guide is for developers, DevOps engineers, and technical startup teams who need to operate containerized applications reliably in production.
You will learn how to inspect and interpret bridge network internals, design predictable IP addressing, control exposed ports with precision, secure inter-container traffic, and automate verification. Every section includes real commands, expected output, and failure signals so you can apply these techniques immediately.
Operational safety is the foundation: observe before changing, limit blast radius, use placeholders instead of secrets, verify each change, and document recovery steps. By the end, you will be able to troubleshoot bridge networking issues with confidence and prevent common misconfigurations.
Core Bridge Networking Mechanics
Docker's default bridge network (named bridge) is a virtual Layer 2 network that connects containers on the same host. Each container gets a virtual Ethernet interface (veth) that is paired with a host-side interface attached to the docker0 bridge. The bridge acts like a physical switch, forwarding frames between interfaces based on MAC addresses.
Key facts about the default bridge:
- It has the subnet
172.17.0.0/16by default (configurable via daemon.json). - Containers on the default bridge can communicate with each other by IP address only, not by container name. DNS resolution is not provided on this network.
- External access requires publishing ports with
-por--publish. - The bridge itself has an IP on the host, typically
172.17.0.1, which containers use as their default gateway.
In contrast, user-defined bridge networks (created with docker network create) provide automatic DNS resolution between containers, better isolation, and the ability to connect/disconnect containers on the fly. For anything beyond a single throwaway container, use a user-defined bridge.
Create a user-defined bridge:
docker network create --driver bridge --subnet 10.5.0.0/16 --gateway 10.5.0.1 my_app_net
Expected output: the network ID, e.g. 3f0a9b2c8d1e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a.
Now run two containers on this network:
docker run -d --name web --network my_app_net --ip 10.5.0.10 nginx:alpine
docker run -d --name app --network my_app_net --ip 10.5.0.20 alpine sleep 3600
From inside app, ping web by name:
docker exec -it app ping -c 2 web
Expected output shows successful replies with 10.5.0.10. This works because Docker's embedded DNS server (at 127.0.0.11 inside each container) resolves container names on user-defined networks.
Inspecting and Decoding Network State
Before making any change, gather evidence. The primary command is docker network inspect <network>, which returns a JSON object with containers, IP assignments, and network options.
Inspect our custom network:
docker network inspect my_app_net
Look for the Containers section. You will see entries like:
"Containers": {
"1a2b3c...": {
"Name": "web",
"IPv4Address": "10.5.0.10/16",
"MacAddress": "02:42:ac:11:00:02"
},
"4d5e6f...": {
"Name": "app",
"IPv4Address": "10.5.0.20/16"
}
}
To see the host-side bridge configuration:
ip addr show docker0
brctl show docker0
docker0 shows the bridge IP 172.17.0.1/16. brctl show lists interfaces attached to the bridge (the veth pairs for default bridge containers).
For a specific container, inspect its network settings:
docker inspect web --format '{{json .NetworkSettings.Networks}}' | jq
This reveals the IP, gateway, MAC address, and aliases. If jq is not installed, omit it or use python -m json.tool.
Check DNS resolution inside a container:
docker exec web cat /etc/resolv.conf
Expected output on a user-defined network:
nameserver 127.0.0.11
options ndots:0
This confirms the container uses Docker's embedded DNS.
Version and Environment Inventory
Before troubleshooting or changing bridge network settings, catalog the environment. Record the Docker version, daemon configuration, existing networks, and running containers.
Check Docker version:
docker version --format '{{.Server.Version}}'
Example output: 24.0.7. Note that features like --ip require a user-defined network, while some flags depend on the daemon's IPAM driver.
List all networks:
docker network ls
Expected output includes bridge, host, none, and any custom networks. The SCOPE column shows local for bridge networks.
For each running container, get its network attachment:
docker ps --format 'table {{.Names}} {{.Networks}} {{.Ports}}'
Example:
NAMES NETWORKS PORTS
web my_app_net 80/tcp
app my_app_net
Check iptables rules created by Docker for port publishing and inter-container communication:
sudo iptables -t nat -L -n -v | grep DOCKER
sudo iptables -L -n -v | grep DOCKER
These rules are critical for routing traffic to containers. Misconfigured firewalls often break connectivity.
Document the current state in a text file or runbook before any change. For example:
Date: 2025-04-08
Docker version: 24.0.7
Networks: bridge (172.17.0.0/16), my_app_net (10.5.0.0/16)
Containers: web (10.5.0.10), app (10.5.0.20)
Safe Configuration Changes
When altering bridge network settings, change one item at a time and verify immediately. Use placeholder values for secrets and avoid exposing sensitive ports to all interfaces.
Creating a User-Defined Bridge with a Specific Subnet
Instead of relying on the default subnet, define an explicit range to avoid conflicts with corporate VPNs or other networks.
Example:
docker network create --driver bridge \
--subnet 192.168.100.0/24 \
--gateway 192.168.100.1 \
--ip-range 192.168.100.128/25 \
--opt com.docker.network.bridge.name=br_custom \
custom_net
Explanation:
--subnetdefines the full network.--gatewaysets the bridge IP on the host.--ip-rangerestricts automatic IP assignment to a smaller pool (useful for reserving static IPs).--opt com.docker.network.bridge.namerenames the host bridge interface tobr_customfor clarity.
Verify creation:
docker network inspect custom_net --format '{{json .IPAM.Config}}'
Expected output:
[{"Subnet":"192.168.100.0/24","Gateway":"192.168.100.1"}]
Assigning Static IPs to Containers
For services that need a stable address (e.g., database accessed by multiple apps), assign a static IP.
Run a database container:
docker run -d --name db \
--network custom_net \
--ip 192.168.100.10 \
-e MYSQL_ROOT_PASSWORD=secret \
mysql:8
Verify the IP:
docker inspect db --format '{{.NetworkSettings.Networks.custom_net.IPAddress}}'
Expected: 192.168.100.10.
Caution: On a user-defined bridge, you can mix static and dynamic IPs, but ensure the static IP falls within the subnet and is not in the --ip-range dynamic pool.
Publishing Ports Selectively
By default, -p 8080:80 binds to all host interfaces (0.0.0.0:8080). To restrict access to localhost only:
docker run -d --name local_web -p 127.0.0.1:8080:80 nginx:alpine
Now the service is reachable only from the host itself. Verify:
curl http://127.0.0.1:8080
Expected: HTML from nginx. Attempting to access from another machine will fail.
To bind to a specific host IP (e.g., a private management interface):
docker run -d --name mgmt_web -p 192.168.1.100:8081:80 nginx:alpine
Remember that port publishing works via iptables DNAT rules. If you have ufw or firewalld, they may not see Docker's rules and can incorrectly expose ports. Always test from an external host to confirm exposure.
Verification and Diagnostics
Effective diagnostics combine packet-level checks, DNS tests, and log analysis. Here is a structured approach.
Connectivity Testing Between Containers
Ping is usually blocked by default in many images (ICMP disabled), so use curl or nc instead.
From app, test TCP connectivity to web port 80:
docker exec app sh -c 'nc -zv web 80'
Expected output: web (10.5.0.10:80) open.
If nc is not installed in the container, use a temporary container:
docker run --rm --network my_app_net alpine nc -zv web 80
DNS Resolution Checks
Test name resolution:
docker exec app nslookup web
Expected output includes the IP 10.5.0.10. If this fails, check that both containers are on the same user-defined network. The default bridge does not provide DNS.
Inspecting Container Logs for Network Errors
Application logs often reveal connection issues.
docker logs web --tail 20
Look for lines like connect: connection refused or no route to host.
Monitoring Bridge Traffic
Use tcpdump on the host bridge interface to see traffic flowing between containers:
sudo tcpdump -i br_custom -n port 80
Generate traffic by curling web from app:
docker exec app wget -qO- http://web
You should see packets in the tcpdump output.
Checking iptables Rules for Port Publishing
To trace how an external connection reaches a container:
sudo iptables -t nat -L DOCKER -n --line-numbers
Find the rule corresponding to the published port. Example:
4 DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:8080 to:172.17.0.2:80
Then check the filter chain for the forward rule:
sudo iptables -L DOCKER -n --line-numbers
If the port is not reachable from outside, ensure the container is actually listening on the expected port inside (e.g., ss -tlnp inside the container).
Failure Modes and Recovery
Bridge networking can fail in predictable ways. Here are common scenarios and how to recover.
Container Cannot Reach the Internet
Symptoms: curl http://example.com inside container hangs or fails with Could not resolve host or Connection timed out.
Possible causes:
- IP forwarding disabled on host:
sysctl net.ipv4.ip_forwardshould be1. If not, runsudo sysctl -w net.ipv4.ip_forward=1and make it permanent in/etc/sysctl.conf. - Firewall blocking outbound NAT: ensure masquerade rule exists:
sudo iptables -t nat -L POSTROUTING -n -v | grep MASQUERADE. If missing, restart Docker. - DNS misconfiguration: container's
/etc/resolv.confpoints to a non-existent server. Usedocker run --dns 8.8.8.8or configure daemon DNS.
Recovery: restart Docker daemon after making changes, then recreate containers.
Inter-Container Communication Fails on Default Bridge
Remember that the default bridge does not provide name resolution. Use IP addresses or switch to a user-defined network.
To migrate a running container to a user-defined network:
docker network connect my_app_net web
But note: you cannot disconnect from the default bridge without recreating the container. For production, define networks in Compose or Dockerfile.
Port Conflict on Host
When publishing a port already in use, Docker returns an error:
Bind for 0.0.0.0:8080 failed: port is already allocated.
Find the conflicting process:
sudo lsof -i :8080
Then choose a different host port or stop the conflicting process.
IP Address Exhaustion
If you run many containers with static IPs and exhaust the subnet, new containers fail to start with no available IPv4 addresses on this network. Increase the subnet size or use a larger range. For example, instead of /24, use /22.
To inspect current IP usage:
docker network inspect custom_net --format '{{range .Containers}}{{.IPv4Address}}{{"\n"}}{{end}}'
Orphaned veth Interfaces After Container Removal
Normally, Docker cleans up veth pairs when a container stops. If orphaned interfaces remain (e.g., after a crash), remove them manually:
ip link show type veth
sudo ip link delete vethXXXXXX
But first ensure the interface is not in use.
Common Pitfalls and How to Avoid Them
- Using the default bridge for multi-container apps. It lacks DNS and isolates poorly. Always create a user-defined bridge for related services.
- Publishing ports to all interfaces. Exposing a database port on
0.0.0.0is a security risk. Bind to127.0.0.1or a private IP. - Ignoring IP address management. Overlapping subnets with host or VPN networks cause routing conflicts. Define explicit subnets.
- Assuming
pingworks. Many images lack ping. Usecurlorncfor TCP checks. - Forgetting to persist iptables rules. Docker's iptables rules are lost on daemon restart if not managed properly. Use
--iptables=true(default) and avoid flushing Docker chains. - Not checking DNS resolution. In user-defined networks, DNS is automatic, but custom
--dnsor--dns-searchoptions can override it and break name resolution. - Relying on container IPs for external access. Container IPs are not routable from outside the host. Always publish ports and access via host IP.
Operations Checklist
Use this checklist before and after making bridge network changes.
- [ ] Record Docker version, current networks, and container IP assignments using
docker version,docker network ls, anddocker network inspect. - [ ] Identify the blast radius: which containers, services, or external clients are affected?
- [ ] For any change to a shared network, schedule a maintenance window and notify stakeholders (owner: DevOps lead, reviewed weekly).
- [ ] Create a backup of network configuration if using external tools (e.g.,
docker network inspect my_app_net > network_backup.json). - [ ] Apply the smallest change: create a new network, reconnect one container, or adjust a port binding.
- [ ] Verify connectivity with TCP checks (
nc -zv), DNS resolution (nslookup), and application logs. - [ ] Test from an external host to confirm port publishing and security boundaries.
- [ ] Document the change, the verification result, and rollback steps in the runbook (owner: platform engineer, updated immediately).
- [ ] Monitor for 24 hours post-change for unexpected latency or errors.
Conclusion
Docker bridge networking is simple on the surface but hides complexity in IP allocation, DNS, iptables, and interface management. By mastering inspection commands, understanding the differences between default and user-defined bridges, and adopting a systematic diagnostic approach, you can resolve most issues without guesswork.
Start with one low-risk verification: inspect your current networks, test inter-container DNS on a user-defined bridge, and confirm port bindings are scoped correctly. Record the state, make a single change, and verify. As you gain confidence, incorporate these practices into your team's operational runbooks.
The goal is not just to fix problems when they occur, but to design bridge networks that are predictable, secure, and easy to troubleshoot. With the commands and checklists in this guide, you have a solid foundation for operating containerized applications at scale.