## Intro

Docker networking is a frequent source of frustration in containerized environments. A container that cannot reach its database, a service that cannot resolve another service by name, or a port mapping that refuses to work can halt development and disrupt production. This article focuses on common Docker networking errors and provides practical fixes with concrete examples. It is written for developers, DevOps consultants, and technical startup teams who need to move from an observed problem to a verified result quickly.

We will cover the most common failure modes, including bridge networking misconfigurations, overlay network issues in Swarm, DNS resolution failures, port publishing problems, and IPv6 mishaps. For each, we show how to diagnose the issue using standard Docker commands, understand the root cause, apply the minimal safe fix, and verify the outcome. The goal is operational safety: observe before changing, limit the blast radius, use placeholders instead of secrets, and document recovery steps.

Throughout the article, we emphasize a systematic approach:

- **Inventory the environment**: know the Docker version, topology, and current state.
- **Read-only diagnostics**: use `docker network inspect`, `docker logs`, `docker exec`, and other commands to gather evidence.
- **Minimal change**: adjust one scoped configuration item at a time.
- **Verify**: confirm the fix with a specific command and expected output.

By the end, you will have a set of repeatable troubleshooting procedures and a checklist to prevent recurring networking problems.

## Version and Environment Inventory

Before diving into fixes, establish a baseline. Knowing your Docker version, storage driver, network plugins, and container runtime helps you avoid applying advice meant for a different setup. Run the following commands and record the output:

```bash
# Docker version
$ docker version --format '{{.Server.Version}}'
24.0.7

# Docker info (focus on network-relevant sections)
$ docker info --format '{{json .Plugins.Network}}'
["bridge","host","overlay","macvlan"]

# List running containers with their networks
$ docker ps --format 'table {{.Names}}	{{.Networks}}	{{.Ports}}'
NAMES               NETWORKS            PORTS
web                 bridge              0.0.0.0:8080->80/tcp
db                  myapp_default       
```

For Compose projects, use `docker compose ps` and inspect the generated network:

```bash
$ docker compose ps
NAME                COMMAND                  SERVICE             STATUS              PORTS
myapp_web_1         "docker-entrypoint.s…"   web                 running             0.0.0.0:8080->80/tcp
myapp_db_1          "docker-entrypoint.s…"   db                  running             5432/tcp

$ docker network ls | grep myapp
NETWORK ID     NAME           DRIVER    SCOPE
abc123def456   myapp_default  bridge    local
```

If you suspect a network plugin issue, list all networks with their drivers and scope:

```bash
$ docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
1234567890ab   bridge    bridge    local
cdef12345678   host      host      local
90abcdef1234   none      null      local
```

A production-like local test should include a restart test: stop the container, recreate it, and confirm the application still sees the expected network and files. If connectivity breaks after a restart, you likely have a configuration that depends on ephemeral container IPs or a missing network definition.

## Common Error 1: Container Cannot Reach the Internet or External Hosts

### Symptom

A container cannot `ping` an external IP (e.g., 8.8.8.8) or `curl` a public URL. The error might be `ping: bad address` or `curl: (6) Could not resolve host`, indicating DNS or routing failure.

### Diagnosis

Start by checking the container's network settings:

```bash
$ docker exec mycontainer ip addr show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
2: eth0@if10: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever
```

If the container has no IP or the interface is down, something is wrong with the network creation. Check the container's network attachment:

```bash
$ docker inspect --format '{{json .NetworkSettings.Networks}}' mycontainer
{"bridge":{"IPAMConfig":null,"Links":null,"Aliases":null,"NetworkID":"1234567890ab","EndpointID":"","Gateway":"","IPAddress":"","IPPrefixLen":0,"IPv6Gateway":"","GlobalIPv6Address":"","GlobalIPv6PrefixLen":0,"MacAddress":"","DriverOpts":null}}
```

An empty IPAddress suggests the container did not join the network properly. Attempt to connect it explicitly:

```bash
$ docker network connect bridge mycontainer
```

If the container still has no connectivity, inspect the Docker daemon's DNS settings. The daemon uses the host's `/etc/resolv.conf` by default, but you can override it:

```bash
$ docker network inspect bridge
[
    {
        "Name": "bridge",
        "Id": "1234567890ab",
        "Created": "2024-01-01T00:00:00.000000000Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "172.17.0.0/16",
                    "Gateway": "172.17.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "...": {}
        },
        "Options": {
            "com.docker.network.bridge.default_bridge": "true",
            "com.docker.network.bridge.enable_icc": "true",
            "com.docker.network.bridge.enable_ip_masquerade": "true",
            "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
            "com.docker.network.bridge.name": "docker0",
            "com.docker.network.driver.mtu": "1500"
        },
        "Labels": {}
    }
]
```

If `"Internal": true`, the network is isolated from external traffic. If DNS resolution fails, check the container's `/etc/resolv.conf`:

```bash
$ docker exec mycontainer cat /etc/resolv.conf
nameserver 127.0.0.11
options ndots:0
```

The embedded DNS server at 127.0.0.11 should be present. If not, the container was started with `--dns` or `--dns-search` overrides that may be incorrect.

### Fix

1. If the container is on a custom network with `internal: true`, either recreate the network without that flag or connect the container to an external network.
2. If DNS is misconfigured, start the container with explicit DNS servers:

   ```bash
   $ docker run -d --name mycontainer --dns 8.8.8.8 --dns 1.1.1.1 myimage
   ```

3. If the default bridge network is causing issues, create a user-defined bridge network, which provides automatic DNS resolution between containers:

   ```bash
   $ docker network create mynet
   $ docker run -d --name mycontainer --network mynet myimage
   ```

4. Verify connectivity:

   ```bash
   $ docker exec mycontainer ping -c 4 8.8.8.8
   PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
   64 bytes from 8.8.8.8: icmp_seq=1 ttl=114 time=18.6 ms
   ...
   ```

## Common Error 2: Containers Cannot Communicate with Each Other on a User-Defined Bridge Network

### Symptom

Two containers on the same user-defined bridge network cannot resolve each other by name or connect via IP. For example, a web container cannot reach `db` at `db:5432`.

### Diagnosis

First, confirm both containers are on the same network:

```bash
$ docker network inspect mynet
[
    {
        "Name": "mynet",
        "Id": "...",
        "Containers": {
            "container1_id": {
                "Name": "web",
                "IPv4Address": "172.18.0.2/16",
                "IPv6Address": ""
            },
            "container2_id": {
                "Name": "db",
                "IPv4Address": "172.18.0.3/16",
                "IPv6Address": ""
            }
        }
    }
]
```

If one container is missing, connect it:

```bash
$ docker network connect mynet web
```

Check if the containers can ping each other by IP:

```bash
$ docker exec web ping -c 4 172.18.0.3
PING 172.18.0.3 (172.18.0.3) 56(84) bytes of data.
64 bytes from 172.18.0.3: icmp_seq=1 ttl=64 time=0.123 ms
...
```

If IP ping works but name resolution fails, test with `nslookup` or `getent`:

```bash
$ docker exec web nslookup db
Server:         127.0.0.11
Address:        127.0.0.11#53

Name:   db
Address: 172.18.0.3
```

If `nslookup` fails, the embedded DNS server may be having issues, or the container's `/etc/resolv.conf` may not point to 127.0.0.11.

A common mistake is using the default bridge network, which does not provide automatic DNS resolution. On the default bridge, containers can only communicate via IP addresses (unless `--link` is used, which is deprecated).

### Fix

1. Create a user-defined bridge network and attach both containers:

   ```bash
   $ docker network create mynet
   $ docker network connect mynet web
   $ docker network connect mynet db
   ```

2. Alternatively, recreate the containers with the network specified at startup:

   ```bash
   $ docker run -d --name web --network mynet webimage
   $ docker run -d --name db --network mynet dbimage
   ```

3. Test name resolution again:

   ```bash
   $ docker exec web ping -c 1 db
   PING db (172.18.0.3) 56(84) bytes of data.
   64 bytes from db.mynet (172.18.0.3): icmp_seq=1 ttl=64 time=0.089 ms
   ```

## Common Error 3: Port Mapping Not Working or Port Already in Use

### Symptom

A container is started with `-p 8080:80`, but accessing `localhost:8080` fails, or the container fails to start with `Bind for 0.0.0.0:8080 failed: port is already allocated`.

### Diagnosis

Check if the port is already in use on the host:

```bash
$ sudo ss -tulpn | grep :8080
tcp   LISTEN 0      128          0.0.0.0:8080      0.0.0.0:*    users:(("docker-proxy",pid=1234,fd=4))
```

Another container may be using the same port. List port mappings:

```bash
$ docker ps --format '{{.Names}}	{{.Ports}}'
web1                0.0.0.0:8080->80/tcp
web2                0.0.0.0:8080->80/tcp  # This will fail to start
```

If you are using a firewall, ensure the port is allowed. Also, check if the container process is actually listening on the container's port:

```bash
$ docker exec web1 netstat -tulpn | grep :80
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      1/nginx: master p
```

### Fix

1. Use a different host port:

   ```bash
   $ docker run -d --name web2 -p 8081:80 webimage
   ```

2. Or use dynamic port mapping and query the assigned port:

   ```bash
   $ docker run -d --name web2 -P webimage
   $ docker port web2
   80/tcp -> 0.0.0.0:49153
   ```

3. If a port must be freed, identify and stop the process using it, then restart the container.

## Common Error 4: Overlay Network Issues in Docker Swarm

### Symptom

In a Swarm cluster, services on different nodes cannot communicate over an overlay network. Errors like `No route to host` or name resolution failure occur.

### Diagnosis

First, ensure the overlay network exists and is attached to the services:

```bash
$ docker network ls | grep overlay
NETWORK ID     NAME              DRIVER    SCOPE
xyz123abc456   myoverlay         overlay   swarm

$ docker service inspect --format '{{json .Spec.TaskTemplate.Networks}}' myservice
[{"Target":"xyz123abc456"}]
```

Check that all nodes have the necessary ports open for overlay networking: TCP 2377 for cluster management, TCP/UDP 7946 for node communication, and UDP 4789 for VXLAN data.

On each node, verify the VXLAN interface exists:

```bash
$ ip link show type vxlan
10: vxlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master br0 state UNKNOWN mode DEFAULT group default 
    link/ether 02:42:... brd ff:ff:ff:ff:ff:ff
```

If the VXLAN interface is missing or the MTU is incorrect, it can cause fragmentation and connectivity issues.

### Fix

1. Ensure firewall rules allow the required ports.
2. If MTU issues are suspected, adjust the MTU on the docker daemon or network. For example, if the underlying network has an MTU of 1400, set the Docker MTU to 1400:

   ```bash
   $ sudo dockerd --mtu 1400
   ```

   Or set it per network:

   ```bash
   $ docker network create -d overlay --opt com.docker.network.driver.mtu=1400 myoverlay
   ```

3. Restart the Docker daemon on affected nodes.

## Common Error 5: DNS Resolution Failures for Custom Domains

### Symptom

A container cannot resolve a custom internal domain, e.g., `api.internal.example.com`.

### Diagnosis

Check the container's DNS configuration:

```bash
$ docker exec mycontainer cat /etc/resolv.conf
search mydomain.local
nameserver 127.0.0.11
options ndots:0
```

If the custom domain is served by an external DNS server, the container may not be querying it. Test with `nslookup` using the specific server:

```bash
$ docker exec mycontainer nslookup api.internal.example.com 10.0.0.53
```

If that works but default resolution does not, the Docker DNS may not forward to the right server.

### Fix

Start the container with the `--dns` and `--dns-search` options:

```bash
$ docker run -d --name mycontainer --dns 10.0.0.53 --dns 8.8.8.8 --dns-search mydomain.local myimage
```

Alternatively, configure the Docker daemon to use a custom DNS server globally by editing `/etc/docker/daemon.json`:

```json
{
  "dns": ["10.0.0.53", "8.8.8.8"],
  "dns-search": ["mydomain.local"]
}
```

Then restart Docker:

```bash
$ sudo systemctl restart docker
```

## Common Error 6: IPv6 Configuration Problems

### Symptom

Containers do not get IPv6 addresses or cannot communicate over IPv6.

### Diagnosis

Check if IPv6 is enabled on the network:

```bash
$ docker network inspect bridge | grep EnableIPv6
"EnableIPv6": false,
```

By default, IPv6 is disabled. If you need IPv6, you must enable it in the daemon configuration and create networks with IPv6 subnets.

### Fix

1. Edit `/etc/docker/daemon.json` to enable IPv6 and set a fixed CIDR:

   ```json
   {
     "ipv6": true,
     "fixed-cidr-v6": "2001:db8:1::/64"
   }
   ```

2. Restart Docker:

   ```bash
   $ sudo systemctl restart docker
   ```

3. Create a network with IPv6 support:

   ```bash
   $ docker network create --ipv6 --subnet=2001:db8:1::/64 myipv6net
   ```

4. Run a container on that network and check its IPv6 address:

   ```bash
   $ docker run -d --name ipv6test --network myipv6net alpine sleep 300
   $ docker exec ipv6test ip -6 addr show eth0
   2: eth0@if11: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 state UP 
       inet6 2001:db8:1::242:ac11:2/64 scope global nodad 
          valid_lft forever preferred_lft forever
   ```

## Common Pitfalls and How to Avoid Them

1. **Using the default bridge network for production**. The default bridge lacks automatic service discovery and isolation. Always create user-defined networks for applications.
   - **Why it happens**: convenience; `docker run` without `--network` defaults to bridge.
   - **How to avoid**: define networks in Compose files or create custom networks explicitly.

2. **Hard-coding container IPs**. Container IPs can change after recreation. Rely on DNS names instead.
   - **Why it happens**: Developers may inspect and copy IPs.
   - **How to avoid**: use service names; if needed, assign static IPs via `--ip` on a user-defined network.

3. **Ignoring MTU mismatches**. Overlay networks over VPNs or certain cloud providers may require lower MTU.
   - **Why it happens**: default MTU 1500 may not work if underlying network has lower MTU.
   - **How to avoid**: test with `ping -M do -s 1472 <host>` to discover max MTU and configure accordingly.

4. **Not cleaning up unused networks**. Unused networks can clutter the environment and cause confusion.
   - **Why it happens**: forgetting to remove networks after experiments.
   - **How to avoid**: periodically run `docker network prune`.

5. **Overlooking firewall rules**. For Swarm or multi-host setups, missing firewall ports cause mysterious failures.
   - **Why it happens**: assuming all traffic is allowed.
   - **How to avoid**: document required ports and verify with `nc -zv <host> <port>`.

## Operations Checklist

Use this checklist before and after making network changes:

- [ ] Record current state: `docker network ls`, `docker ps`, `docker info`.
- [ ] Identify the error: collect exact error messages from logs and commands.
- [ ] Isolate the issue: test connectivity with `ping`, `curl`, `nslookup`, and `traceroute`.
- [ ] Check network configuration: `docker network inspect <network>`.
- [ ] Check DNS settings: `cat /etc/resolv.conf` inside container.
- [ ] Check port mappings: `docker port <container>`.
- [ ] Verify firewall rules: ensure required ports are open.
- [ ] Make one change at a time and test after each change.
- [ ] Document the change and rollback plan.
- [ ] Verify the fix: repeat the original failing command and confirm success.
- [ ] Update monitoring and alerting if necessary.

## Conclusion

Docker networking errors can be daunting, but a systematic approach simplifies troubleshooting. By establishing a baseline, using the right diagnostic commands, understanding common failure patterns, and applying minimal fixes with verification, you can resolve most networking issues quickly. Always prefer user-defined bridge networks for container-to-container communication, avoid hard-coding IPs, and pay attention to DNS and MTU settings. Use the operations checklist to prevent future problems and ensure a reliable container environment. With practice, you will navigate Docker networking with confidence.