## Intro

Docker Context is a powerful feature that lets you switch between different Docker endpoints, such as a local Docker Desktop engine, a remote Docker daemon over SSH, or a cloud provider's managed container service. However, networking problems can quickly turn this convenience into confusion. Containers may fail to reach external services, DNS lookups may time out, published ports may refuse connections, or traffic may be silently dropped by a firewall.

This guide provides a systematic approach to diagnosing and fixing Docker Context networking issues, with concrete commands and expected outputs. Whether you are a developer working locally or a DevOps consultant managing remote contexts, the steps here will help you isolate the problem and restore connectivity quickly.

## Version and Environment Inventory

Before changing any settings, gather information about your Docker client, contexts, daemon, and network topology. This inventory prevents misdirected troubleshooting and ensures you understand the scope of the problem.

### Check Docker client and server versions

Run `docker version` to see the client and server versions, plus the operating system and architecture. For remote contexts, the server section reflects the remote daemon. Example output (truncated):

```
Client: Docker Engine - Community
 Version:           24.0.5
 API version:       1.43
 Go version:        go1.20.4
 Git commit:        ced0996
 Built:             Fri Jul 21 19:36: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.4
  Git commit:       a61e2b4
  Built:            Fri Jul 21 19:36:24 2023
  OS/Arch:          linux/amd64
  Experimental:     false
```

Note the `Context:` line shows the currently active context. If the server version differs significantly from the client, compatibility issues may arise, but networking problems are usually independent of minor version differences.

### List Docker contexts

`docker context ls` shows all configured contexts and their endpoints. Example:

```
NAME                TYPE    DESCRIPTION                         DOCKER ENDPOINT               KUBERNETES ENDPOINT   ORCHESTRATOR
default *           moby    Current DOCKER_HOST based config   unix:///var/run/docker.sock                         swarm
remote-ssh          ssh     Remote Docker host via SSH         ssh://user@host                                     
```

The asterisk marks the active context. The `DOCKER ENDPOINT` column reveals how the client connects to the daemon: a local Unix socket (`unix:///var/run/docker.sock`) or a remote protocol (`ssh://`, `tcp://`, etc.). If you are troubleshooting a remote context, ensure the endpoint is reachable before diving into container networking.

### Test basic connectivity to the daemon

For a remote SSH context, verify SSH connectivity and the daemon's responsiveness:

```bash
docker --context remote-ssh info
```

Expected output includes the server's OS, kernel version, number of containers and images, and other details. If this command fails, the problem is likely with SSH authentication or daemon access, not container networking.

### Understand the network topology

Document where containers are running (local engine, remote host, cloud) and how you expect traffic to flow. Identify the Docker network driver in use (bridge, overlay, host, macvlan, etc.) with:

```bash
docker network ls
```

Example output:

```
NETWORK ID     NAME      DRIVER    SCOPE
1a2b3c4d5e6f   bridge    bridge    local
7f8g9h0i1j2k   host      host      local
3l4m5n6o7p8q   none      null      local
```

This step ensures that subsequent diagnostic commands are run against the correct context and network.

## Safe Configuration Path

When changes are needed, make them scoped and reversible. Avoid modifying the Docker daemon's global settings unless absolutely necessary; prefer per-container or per-network adjustments.

### Use explicit contexts to avoid accidental changes

Always prefix commands with `--context <name>` when working with non-default contexts, or set `DOCKER_CONTEXT` in your shell. For example:

```bash
export DOCKER_CONTEXT=remote-ssh
```

This ensures that every Docker command in that shell targets the intended daemon, preventing local/remote mix-ups.

### Create a dedicated network for isolated testing

Instead of altering the built-in bridge network, create a custom bridge network for your containers. This gives you control over subnet and gateway, and avoids disrupting other containers.

```bash
docker network create --driver bridge --subnet 172.28.0.0/16 --gateway 172.28.0.1 testnet
```

Expected output: the network ID. Use this network for troubleshooting so that changes are contained.

### Run a diagnostic container with explicit network settings

Start a container attached to your test network with a known image, and override DNS if needed. For example, to test DNS resolution inside the network:

```bash
docker run -it --rm --network testnet --dns 8.8.8.8 alpine nslookup example.com
```

This command uses Google's public DNS (8.8.8.8) but you can substitute your internal DNS. If the container cannot resolve, the output shows timeout or server failure, indicating a network path issue.

### Publish ports explicitly and test locally

When exposing a service, bind to a specific host interface to avoid unintended exposure. Use the `-p` flag with an IP address:

```bash
docker run -d --name web --network testnet -p 127.0.0.1:8080:80 nginx
```

This maps host loopback port 8080 to container port 80. Verify with `curl http://127.0.0.1:8080` - you should see the nginx welcome page. If this fails, the issue may be with the container's service or port mapping.

### Adjust firewall rules only when necessary

If connections are refused from outside the host, check firewall settings. For Linux hosts using iptables, Docker normally inserts its own rules. However, some firewall tools (like UFW or firewalld) may block Docker traffic. A safe temporary test is to allow traffic on the published port:

```bash
sudo ufw allow 8080/tcp
```

Then test connectivity. Remember that disabling the firewall entirely is risky; instead, add specific rules and remove them if not needed.

## Verification and Diagnostics

With the configuration in place, verify connectivity step by step. Start from inside the container and work outward.

### Inside the container: check interfaces and routes

Run `ip addr` and `ip route` inside a running container (install iproute2 if needed). Example from an Alpine container:

```bash
docker exec -it web ip addr show eth0
```

Expected output includes an IP address from your custom subnet (e.g., 172.28.0.2/16) and the MAC address. Then:

```bash
docker exec -it web ip route
```

Output:

```
default via 172.28.0.1 dev eth0
172.28.0.0/16 dev eth0 proto kernel scope link src 172.28.0.2
```

This confirms the container has a valid IP and default route through the network gateway.

### DNS resolution tests

Use `nslookup` or `dig` (install bind-tools in Alpine) to test name resolution from inside the container:

```bash
docker exec -it web nslookup example.com
```

Expected output includes the resolved IP address and the DNS server used. If the server is unreachable, you may see:

```
;; connection timed out; no servers could be reached
```

This indicates a DNS configuration or network path problem.

Check the container's `/etc/resolv.conf` to see which DNS servers are configured:

```bash
docker exec -it web cat /etc/resolv.conf
```

Typical output for a user-defined network:

```
search myproject.local
nameserver 127.0.0.11
options ndots:0
```

The `127.0.0.11` is Docker's embedded DNS server, which forwards queries to the host's configured DNS. If this is missing or incorrect, the container will fail to resolve.

### Test outbound connectivity

From inside the container, attempt to reach an external service by IP and by name:

```bash
docker exec -it web ping -c 4 8.8.8.8
```

If ping is not available, install it or use `wget`/`curl`. For name-based test:

```bash
docker exec -it web wget -O- http://example.com
```

If IP works but name fails, the problem is DNS. If neither works, check routing and firewall.

### Test inbound connectivity to published ports

From the Docker host, test the published port:

```bash
curl http://localhost:8080
```

If this fails, check that the container is running and listening:

```bash
docker port web
```

Expected output:

```
80/tcp -> 127.0.0.1:8080
```

This confirms the port mapping. If the mapping is correct but the connection is refused, check the container's service logs:

```bash
docker logs web
```

Look for errors like "address already in use" or the service not listening on the expected port.

From an external machine (if accessing the Docker host via network), test with the host's IP:

```bash
curl http://<docker-host-ip>:8080
```

If this fails while localhost succeeds, the firewall is likely blocking external access.

### Diagnose with Docker network inspection

Inspect the network to see connected containers and their IPs:

```bash
docker network inspect testnet
```

Output includes a JSON object with Containers section showing each container's IPv4 address. Verify that your container has an address and that there are no conflicts.

### Use traceroute to find routing issues

From inside the container, trace the path to an external host:

```bash
docker exec -it web traceroute 8.8.8.8
```

If traceroute is not installed, install it or use `mtr`. The output shows each hop; if it stops at the gateway with no further hops, the host may not be forwarding packets (IP forwarding disabled) or a firewall is blocking outbound traffic.

## Failure Modes and Recovery

Common failure modes and how to recover.

### Failure: Container cannot resolve DNS names

Symptoms: `nslookup` times out; applications report "unknown host".

Causes:

- Docker's embedded DNS server not reachable.
- Host's DNS misconfigured.
- Custom DNS settings wrong.

Recovery steps:

- Check `/etc/resolv.conf` inside the container; ensure it points to `127.0.0.11` or valid external DNS.
- Verify the host's DNS resolution works (`nslookup example.com` on the host).
- Explicitly set a working DNS in the container run command (`--dns 8.8.8.8`) to test.
- If using a user-defined network, recreate the network to reset Docker's embedded DNS.

### Failure: Published port not accessible from outside

Symptoms: Can access via localhost on host, but not from other machines.

Causes:

- Firewall blocking the port.
- Container bound to loopback only (`127.0.0.1` instead of `0.0.0.0`).
- Host IP routing issue.

Recovery steps:

- Check container's port binding with `docker port web`; if it shows `127.0.0.1:8080`, re-run the container with `-p 0.0.0.0:8080:80`.
- Add a firewall rule to allow the port.
- Verify the host's IP address and that the service is listening on the correct interface (`ss -tlnp | grep 8080` on the host).

### Failure: Inter-container communication fails on custom network

Symptoms: Containers on the same network cannot reach each other by name or IP.

Causes:

- Containers attached to different networks.
- Embedded DNS name resolution not working.
- Network misconfiguration.

Recovery steps:

- Use `docker network inspect testnet` to confirm both containers are connected.
- Test connectivity by IP first; if IP works but name does not, it's a DNS issue. Ensure containers have unique names and use the correct network alias.
- If IP fails, check the bridge interface on the host (`ip addr show br-<network-id>`).
- Recreate the network and reattach containers if needed.

### Rollback: Revert to original configuration

If a change causes problems, revert:

- Remove custom DNS overrides by re-running the container without `--dns`.
- Delete firewall rules with `sudo ufw delete allow 8080/tcp`.
- Remove the custom network with `docker network rm testnet` (after stopping containers).
- If you modified the daemon's global DNS or iptables settings, restore the previous configuration files and restart Docker.

### Recovery checks after rollback

After reverting, repeat the verification steps from earlier to confirm the original behavior is restored.

## Common Pitfalls and How to Avoid Them

Even experienced Docker users can stumble into networking traps. Here are the most frequent mistakes and how to steer clear.

### Using the wrong context by accident

**Why it happens:** You have multiple contexts configured and forget which one is active. You run `docker run` expecting to deploy on the remote server but actually launch the container on your laptop.

**How to avoid:** Always check `docker context ls` before critical operations, or set a shell prompt that shows the current context. For scripts, explicitly pass `--context <name>`.

**Recovery:** If you deployed a container on the wrong host, stop and remove it immediately with `docker --context <correct-context> rm -f <container>` to avoid resource leaks.

### Ignoring Docker's embedded DNS

**Why it happens:** You assume containers use the host's DNS servers directly and configure them as static external IPs. When the container is recreated on a different host, those IPs may be unreachable.

**How to avoid:** Rely on Docker's built-in DNS (`127.0.0.11`) for service discovery on user-defined networks. Only override DNS with `--dns` when you have a specific internal DNS infrastructure.

**Recovery:** Re-run the container without custom DNS flags; if your application requires a specific resolver, use a configuration file or environment variable instead of hardcoding.

### Binding services to loopback only

**Why it happens:** During development, you use `-p 127.0.0.1:80:80` to avoid exposing the service publicly. Later, when moving to production, you forget to change the binding and wonder why external clients cannot connect.

**How to avoid:** Define port bindings in a compose file or script with a variable, and review the value for each environment. Clearly document which ports are intended for local testing only.

**Recovery:** Re-run the container with `-p 0.0.0.0:80:80` or the appropriate interface IP, then test from another host.

### Firewall rules not reloaded or conflicting

**Why it happens:** You manually add `iptables` rules during troubleshooting but do not persist them. After a reboot, the rules vanish, and connectivity breaks again. Or, a separate firewall manager (like UFW or firewalld) overrides Docker's rules.

**How to avoid:** Use the appropriate persistent firewall tool for your distribution (e.g., `netfilter-persistent` for iptables, UFW's own service). For production, rely on infrastructure-as-code or a configuration management system to manage firewall rules.

**Recovery:** Re-apply the needed rules and ensure they are saved. Check the order of rules with `iptables -L -n -v` to see if any are blocking Docker's chains.

### Overlapping subnets

**Why it happens:** You create a custom network with a subnet that overlaps with the host's LAN or another network, leading to routing conflicts. Traffic intended for external hosts may be routed into the Docker bridge.

**How to avoid:** Choose a private IP range that is not used elsewhere in your infrastructure. Use `docker network inspect` to review existing networks before creating a new one.

**Recovery:** Delete the conflicting network and recreate it with a non-overlapping subnet. Ensure containers are reattached and re-tested.

### Not checking IPv6 support

**Why it happens:** Your container or application attempts to resolve an IPv6 address, but the Docker daemon or host does not have IPv6 enabled. The connection hangs or fails.

**How to avoid:** Know whether IPv6 is required for your services. If not, disable IPv6 in the container or use an IPv4-only network. If needed, enable IPv6 in Docker's daemon configuration and use an IPv6-capable network.

**Recovery:** Check the container's `/etc/resolv.conf` and the application's behavior. If IPv6 is not supported, force IPv4 by adjusting the application or using `--sysctl net.ipv6.conf.all.disable_ipv6=1` (though this may have side effects).

## Operations Checklist

Use this checklist for routine troubleshooting and prevention.

| Step | Action | Command / Expectation |
|------|--------|-----------------------|
| 1 | Confirm active context | `docker context ls` - ensure correct context has `*` |
| 2 | Verify daemon connectivity | `docker info` - returns daemon details |
| 3 | List networks | `docker network ls` - note drivers and names |
| 4 | Inspect container network settings | `docker inspect <container> -f '{{json .NetworkSettings}}'` - check IP, ports, DNS |
| 5 | Test DNS from container | `docker exec <container> nslookup example.com` - should resolve |
| 6 | Test outbound connectivity | `docker exec <container> ping -c 4 8.8.8.8` - should get replies |
| 7 | Test inbound port | `curl http://localhost:<published-port>` on host |
| 8 | Check firewall rules | `sudo ufw status` or `sudo iptables -L -n` |
| 9 | Review logs | `docker logs <container>` - look for network-related errors |
| 10 | Document changes | Keep notes on any modifications for future rollback |

Perform this checklist when investigating any networking problem, and after applying fixes to confirm resolution.

## Conclusion

Docker Context networking issues can be challenging, but a structured approach makes them manageable. Start by inventorying your environment and understanding the context and network topology. Make safe, scoped changes for testing, then systematically verify each layer: container network, DNS, outbound, and inbound. When failures occur, use the recovery steps to roll back or correct. Finally, maintain an operations checklist for repeatable troubleshooting. With these practices, you can quickly diagnose and resolve connectivity problems across any Docker context.

Remember to always test in a controlled manner, document your changes, and verify after every step. This reduces downtime and builds confidence in your Docker deployments.