Intro
Docker networking is a powerful tool, but it is also one of the most common sources of production incidents. A misconfigured bridge network, an overlapping subnet, or an incorrectly published port can cause application outages, connectivity failures, and security vulnerabilities. This guide walks through practical examples of Docker networking mistakes, explains why they happen, and shows you how to diagnose, fix, and avoid them.
This article is written for developers, DevOps engineers, SREs, and technical startup teams who use Docker in development, staging, or production. It assumes you are familiar with basic Docker commands and concepts like containers, images, and Docker Compose, but you may not be an expert in Linux networking or container orchestration.
You will learn how to:
- Perform a version and environment inventory before making changes.
- Follow a safe configuration path with minimal blast radius.
- Use diagnostic commands to verify network connectivity and configuration.
- Recognize common failure modes and recover from them.
- Build an operations checklist to keep your Docker networking healthy.
Each section includes concrete commands, expected outputs, and practical examples. The goal is operational safety: observe before changing, limit the blast radius, use placeholders instead of secrets, verify the result, and document how to recover if the expected state is not reached.
Version and Environment Inventory
Before touching any Docker networking configuration, you need to know exactly what you are working with. This includes the Docker version, the operating system, the container runtime, the network drivers in use, and the current state of your containers and networks.
Start by checking your Docker version. Different versions have different networking features and defaults. For example, Docker 20.10 introduced support for the host-gateway special string in extra hosts, and Docker 23.0 changed the default bridge network behavior in some ways. Run:
docker version
Expected output includes client and server versions, like:
Client: Docker Engine - Community
Version: 24.0.5
API version: 1.43
Go version: go1.20.6
Git commit: ced0996
Built: Fri Jul 21 15:21:24 2023
OS/Arch: linux/amd64
Context: default
Server: Docker Engine - Community
Engine:
Version: 24.0.5
API version: 1.43 (minimum version 1.12)
Go version: go1.20.6
Git commit: a61e2b4
Built: Fri Jul 21 15:20:08 2023
OS/Arch: linux/amd64
Experimental: false
If you are using Docker Desktop on Mac or Windows, the networking model is different because Docker runs inside a lightweight VM. The docker version command will still work, but you should be aware that host networking behaves differently on these platforms.
Next, list the existing networks. Docker creates three networks by default: bridge, host, and none. Custom networks may also exist, especially if you use Docker Compose. Run:
docker network ls
Example output:
NETWORK ID NAME DRIVER SCOPE
9f904ee27bf3 bridge bridge local
95e74588f40d host host local
d5c7d2f0c48d none null local
Take note of the driver column. The bridge driver is used for single-host networking, overlay for multi-host (Swarm), and macvlan or ipvlan for advanced cases. If you see a network with a driver you do not recognize, investigate before proceeding.
Now, check which containers are running and what networks they are attached to:
docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}\t{{.Networks}}"
The {{.Networks}} placeholder is available in newer Docker versions and shows the network names the container is connected to. If your Docker version does not support it, use docker inspect on individual containers.
For a more detailed view of a specific container's network settings, use:
docker inspect <container_name_or_id>
This returns a JSON object with a NetworkSettings section that includes IP addresses, gateway, MAC address, ports, and network names. For example, to get the IP address of a container connected to a custom bridge network:
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' my_container
Expected output might be 172.18.0.2. Note that the IP address is assigned from the network's subnet.
Finally, if you use Docker Compose, check the project status:
docker compose ps
This shows the services, their state, and the ports they publish. Example:
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
myapp_web_1 nginx:alpine "/docker-entrypoint.…" web 2 hours ago Up 2 hours 0.0.0.0:8080->80/tcp
myapp_db_1 postgres:15 "docker-entrypoint.s…" db 2 hours ago Up 2 hours 5432/tcp
Now you have a clear picture of the environment. Record this information in a log or runbook before making any changes. This will help you detect drift and roll back if needed.
Pitfall: Assuming the default bridge network is sufficient. Many developers use the default bridge network for all containers. However, on the default bridge, containers can only communicate by IP address, not by name, unless you use the --link flag (which is legacy and discouraged). This leads to hard-coded IPs and brittle configurations. Instead, always create a custom bridge network for your application stack. We will cover this in the next section.
Safe Configuration Path
When you need to change Docker networking configuration, follow a safe path that minimizes risk and allows quick rollback. This path involves observing the current state, planning a minimal change, applying it in a reversible way, and verifying the result.
Step 1: Identify the Required Change
Before creating or modifying a network, list all containers that will be affected. For example, if you need to move a container from the default bridge to a custom bridge network, ensure no other containers depend on the current IP address or DNS name.
If you are using Docker Compose, review the docker-compose.yml file. A typical service definition may look like this:
version: '3.8'
services:
web:
image: nginx:alpine
ports:
- "8080:80"
networks:
- frontend
db:
image: postgres:15
networks:
- backend
networks:
frontend:
driver: bridge
backend:
driver: bridge
In this example, the web service is on the frontend network, and the db service is on the backend network. They cannot communicate because they are on different networks. If you need the web service to access the database, you must attach both services to a common network or create a connection between the networks.
Step 2: Create a Custom Bridge Network
Instead of modifying existing networks, create a new one with a descriptive name and a specific subnet to avoid conflicts. Use the docker network create command with explicit options:
docker network create \
--driver bridge \
--subnet 172.28.0.0/16 \
--gateway 172.28.0.1 \
myapp_network
Expected output: the network ID, e.g., f7b3c1d9e4a5...
Check that the network was created:
docker network inspect myapp_network
You should see the subnet and gateway in the IPAM section, and an empty Containers object until you attach containers.
Step 3: Attach a Running Container to the Network
You can attach a running container to a new network without stopping it using docker network connect. However, be careful because this adds a new network interface to the container, which may affect routing or cause conflicts if the container has static routes.
Example:
docker network connect myapp_network my_container
To verify, inspect the container's networks:
docker inspect -f '{{range $key, $value := .NetworkSettings.Networks}}{{$key}} {{end}}' my_container
Expected output: bridge myapp_network (assuming the container is also on the default bridge).
If you need to disconnect from a network:
docker network disconnect myapp_network my_container
This is useful for rollback.
Step 4: Use Compose for Declarative Changes
If your application uses Docker Compose, modify the docker-compose.yml file to define networks and attach services. Then run:
docker compose up -d
Compose will create networks that do not exist and attach containers accordingly. To apply changes to an existing stack, run docker compose up -d --force-recreate to recreate containers with new network settings. Be aware that this will cause downtime for those containers; plan accordingly.
To rollback a Compose change, revert the docker-compose.yml file to the previous version and run docker compose up -d again. Using version control for Compose files makes this easy.
Step 5: Verify Connectivity
After applying the change, test connectivity between containers. For example, if you attached a web container to a network with a database, test from inside the web container:
docker exec -it web_container ping db_container
If DNS resolution works on the custom network, the ping should succeed. You can also test a TCP connection using nc or curl if available in the container.
If the test fails, check the container's DNS configuration:
docker exec web_container cat /etc/resolv.conf
On a custom bridge network, Docker's embedded DNS server at 127.0.0.11 should be the first entry. If it is not, your container may not resolve other container names.
Pitfall: Changing the default bridge subnet without considering existing containers. The default bridge network uses 172.17.0.0/16 by default. Some administrators change it in the Docker daemon configuration to avoid conflicts with corporate networks. However, existing containers on the old subnet will lose connectivity unless recreated. If you must change the default bridge subnet, stop all containers, edit /etc/docker/daemon.json, and restart Docker, then recreate the containers. This is a high-impact operation and should only be done during a maintenance window.
Verification and Diagnostics
After making network changes, you need to verify that everything works as expected and diagnose any issues that arise. Docker provides several built-in tools for network debugging.
Check Container DNS Resolution
As mentioned earlier, custom bridge networks provide automatic DNS resolution between containers using their names. To test from within a container, use ping or nslookup if available. For example:
docker exec -it container_a ping container_b
Expected output (partial):
PING container_b (172.28.0.3): 56 data bytes
64 bytes from 172.28.0.3: seq=0 ttl=64 time=0.089 ms
If the container does not have ping, use getent:
docker exec container_a getent hosts container_b
Expected output: 172.28.0.3 container_b
If DNS fails, check the network's enable_ipv6 setting or the container's /etc/hosts file for static entries that may override Docker's DNS.
Inspect Network Details
Use docker network inspect to see the network configuration, including subnet, gateway, attached containers, and their IP addresses. For example:
docker network inspect myapp_network
Look for anomalies such as overlapping subnets, missing containers, or incorrect driver options.
Test Port Publishing
If you publish ports from a container to the host, verify that the port is accessible from outside. Run a simple HTTP server in a container:
docker run -d --name web -p 8080:80 nginx:alpine
Check from the host:
curl http://localhost:8080
You should see the default Nginx welcome page HTML. If not, check the container logs:
docker logs web
Possible errors include "Address already in use" if the host port is taken. Use ss -tulpn | grep 8080 on Linux to see what process is using the port.
Debug Overlay Networks
If you use Docker Swarm with overlay networks, you need additional checks. Use docker network inspect to see the overlay network's VIP and peer IPs. To test service discovery, exec into a task container and ping the service name:
docker exec -it <task_container_id> ping my_service
If the ping fails, check the overlay network's encryption settings and ensure the necessary ports (e.g., 4789 UDP for VXLAN) are open between Swarm nodes.
Pitfall: Using localhost inside a container to access another container. In Docker, localhost refers to the container itself, not the host or other containers. If you need to access a service in another container, use the service name (if on the same network) or the host's IP address with the published port. For host access from inside a container, you can use the special DNS name host.docker.internal on Docker Desktop, or the host's IP address on Linux.
Failure Modes and Recovery
Even with careful planning, Docker networking can fail in unexpected ways. This section covers common failure modes, their symptoms, and steps to recover.
Failure 1: IP Address Exhaustion
Symptom: New containers cannot obtain an IP address. You may see errors like "failed to allocate IP address" in container logs or docker run output.
Cause: The network's subnet has no free IP addresses. For example, a small subnet like /29 (only 6 usable addresses) may run out.
Recovery:
- Check the network's subnet and current allocations:
docker network inspect myapp_network --format '{{json .IPAM.Config}}'
docker network inspect myapp_network --format '{{range .Containers}}{{.Name}} {{.IPv4Address}}{{"\n"}}{{end}}'
- If possible, expand the subnet by creating a new network with a larger subnet and moving containers to it. Alternatively, reduce the number of containers or use a larger subnet from the start.
Failure 2: Port Conflict
Symptom: Container fails to start with error "Bind for 0.0.0.0:8080 failed: port is already allocated."
Cause: Another process on the host or another container is already using the same port.
Recovery:
- Find the conflicting process:
ss -tulpn | grep 8080on Linux, ornetstat -ano | findstr 8080on Windows. - Either stop the conflicting process or change the host port in your Docker run command or Compose file.
- For containers that do not need to publish the port to the host, remove the
-pflag or use--exposeinstead.
Failure 3: DNS Resolution Fails
Symptom: Containers cannot resolve service names, but ping by IP address works.
Cause: The container is not connected to the expected network, or the embedded DNS server is not functioning.
Recovery:
- Check which networks the container is on:
docker inspect -f '{{range .NetworkSettings.Networks}}{{.NetworkID}} {{end}}' container_name. - Compare with the target container's networks. If not on a common network, connect it:
docker network connect myapp_network container_name. - Check the container's
/etc/resolv.conffor the Docker DNS server127.0.0.11. - If using a custom DNS, ensure it can resolve Docker names or disable it for that container.
Failure 4: Overlay Network Connectivity Issues in Swarm
Symptom: Services on different Swarm nodes cannot communicate over an overlay network.
Cause: Firewall rules blocking VXLAN traffic (UDP port 4789), mismatched encryption settings, or node clock skew.
Recovery:
- Check firewall rules on all Swarm nodes and open UDP 4789 (and TCP/UDP 7946 for control plane) if necessary.
- Verify encryption settings:
docker network inspect my_overlay --format '{{json .Options}}'looks for{"encrypted":"true"}. - Restart Docker on affected nodes or recreate the overlay network.
Pitfall: Assuming rollback is always possible. Some network changes, like changing the subnet of an existing network, are not directly reversible without recreating the network. Always backup your configuration files (like daemon.json, docker-compose.yml) and take note of the current network state before making changes. Use version control for infrastructure as code.
Operations Checklist
Maintaining healthy Docker networking requires regular checks and proactive management. Here is an operations checklist that you can adopt and adapt. Each item has an owner or frequency, but you should assign a single accountable person for each task in your team.
Monthly Network Audit (Owner: Infrastructure Engineer - for example, Priya Shah)
- [ ] List all Docker networks and verify they are needed:
docker network ls. - [ ] Remove unused networks:
docker network prune(use with caution; this removes all networks not used by at least one container). - [ ] Check for overlapping subnets between Docker networks and corporate networks. Document the subnet plan.
- [ ] Review port publishing for containers. Ensure only necessary ports are exposed to the host. Use
docker ps --format '{{.Names}} {{.Ports}}'. - [ ] Verify that containers that need to communicate are on the same custom network or have appropriate routes.
Quarterly Security Review (Owner: Security Engineer - for example, Marcus Chen)
- [ ] Check container capabilities related to network:
docker inspect --format '{{.HostConfig.CapAdd}}' container_name; removeNET_ADMINunless absolutely required. - [ ] Review network policies: do any containers use
hostnetwork? If so, justify and document the risk. - [ ] Scan for exposed Docker APIs: ensure Docker daemon is not listening on a public port without TLS.
After Every Infrastructure Change (Owner: On-call engineer - rotate weekly)
- [ ] Run
docker network inspect <network_name>on critical networks to verify subnet, gateway, and attached containers. - [ ] Test connectivity between core services using
pingorcurlfrom within containers. - [ ] Check logs for network-related errors:
journalctl -u docker(on systemd systems) or Docker daemon logs.
Ongoing Monitoring (Owner: Monitoring Team - reviewed monthly)
- [ ] Set up alerts for high network latency or packet loss in containers using tools like Prometheus and cAdvisor.
- [ ] Monitor Docker daemon events for network create/connect/disconnect actions:
docker events --filter 'type=network'. - [ ] Keep Docker versions up to date, but test networking changes in staging before production.
By following this checklist, you can catch many networking issues before they cause outages.
Conclusion
Docker networking configuration mistakes are common, but they are also preventable with careful planning, observation, and testing. This guide has covered the essential steps: inventory your environment, follow a safe change process, verify your configuration, understand common failure modes, and maintain a regular operations checklist.
Remember these key points:
- Always create custom bridge networks for application containers to enable DNS resolution and isolation.
- Use explicit subnets to avoid IP conflicts.
- Publish only necessary ports to the host.
- Test connectivity after every change.
- Document your network architecture and keep configuration files in version control.
- Assign ownership for network health checks and reviews.
As a next step, pick one low-risk verification from this guide, such as inspecting a network's subnet or testing DNS resolution between two containers. Run the command, record the output, and compare it with the expected result. Then, add a monthly network audit to your operational calendar.
A reliable technical workflow makes failures visible, protects sensitive values, limits changes to the intended resource, and defines recovery verification before an incident forces the decision.