Virtualization

Fix Docker Container Networking Issues on Windows and macOS (2026)

25 min read

The Back Room Tech is reader-supported. We may earn a commission when you buy through links on our site. Learn more.

Docker network failures often look vague: a timeout, failed lookup, or refused connection. The useful clue is usually one layer deeper. Check container state, DNS, routes, listeners, port mappings, and firewalls in that order.

This workflow uses Docker inspection tools, nicolaka/netshoot, port probes, and packet capture. Your production image stays clean.

Prerequisites

You’ll need:

  • Docker Desktop running on Windows or macOS
  • A terminal with access to the docker command
  • Permission to inspect and run containers
  • The source container, destination container, Docker network, and application port names
  • Internet access to pull nicolaka/netshoot
  • About 500 MB of free disk space for diagnostic images and capture files

The examples use these placeholders:

  • frontend: the source container
  • api: the destination container
  • app-net: a user-defined bridge network
  • 172.20.0.3: an example destination IP address
  • 8080: the application port inside the destination container
  • 3001: an example port published on the host

Replace them with values from your setup. Container IP addresses help during diagnosis, but they’re temporary. Don’t put one in application configuration and expect it to survive container recreation.

These commands apply to Docker Engine and Docker Desktop releases available in 2026. If the failure began after an upgrade, check the Docker Desktop release notes before blaming your Compose file all afternoon.

Test Environment

This flow works with Linux containers running through Docker Desktop on:

Docker Desktop runs Linux containers inside a managed virtual machine. Remember that boundary. The container bridge, forwarding rules, and Docker-managed NAT live inside the VM. They don’t live in the Windows or macOS host network stack.

A wired connection through a reliable 2.5GbE switch or Cat6 cable can rule out flaky Wi-Fi during external tests. It won’t repair Docker DNS or an internal bridge. Copper has limits.

Quick Diagnosis: Test One Layer at a Time

Run these checks before changing configuration:

StepQuestionCommand or testFailure points to
1Are both containers running?docker ps --allContainer lifecycle or application startup
2Are both attached to the same network?docker inspect and docker network inspectNetwork membership
3Is the destination reachable by IP?nc -zv 172.20.0.3 8080Routing, filtering, or listener
4Does its name resolve?nslookup apiDocker DNS or wrong service name
5Is the application listening?ss -lntp in the destination namespaceApplication bind address or port
6Is the host port published correctly?docker port apiPort mapping
7Is the host port already occupied?Host netstat, Get-NetTCPConnection, or lsofPort conflict
8Does the container have a default route?ip routeGateway or network configuration
9Do packets reach the destination?tcpdumpFirewall, NAT, or return path

Don’t rely on ping alone. Some images omit it, and firewalls may block ICMP. A broken application can also answer pings perfectly well. Probe the real TCP or UDP port with nc, curl, or the application’s client.

1. Confirm Container Status

Start with the dull check: make sure both containers exist and are running.

docker ps --all

Expected output resembles:

CONTAINER ID IMAGE COMMAND STATUS PORTS NAMES
a31f2c9a4b82 example/web “/app/start” Up 8 minutes 0.0.0.0:3001->8080/tcp api
b909eec89121 example/ui “/app/start” Up 8 minutes frontend

A destination marked Exited, Restarting, or Created can’t accept connections reliably. Check its latest output:

docker logs --tail 100 api

If it exited, inspect its state and exit code:

docker inspect api --format 'status={{.State.Status}} exit={{.State.ExitCode}} error={{.State.Error}}'

Expected output for a healthy container:

status=running exit=0 error=

An exit code won’t diagnose the network. It does stop you from troubleshooting a listener that no longer exists.

2. Confirm Network Membership

List the available networks:

docker network ls

Expected output:

NETWORK ID NAME DRIVER SCOPE
14bc7ef91e41 bridge bridge local
75915a8d943e host host local
fa02d9892a33 none null local
e926f91b7220 app-net bridge local

PowerShell showing docker network ls with the default bridge and app-net user-defined bridge, including the NAME and DRIVER columns
macOS Terminal showing docker network ls with the default bridge and app-net user-defined bridge, including the NAME and DRIVER columns

Show each container’s attached networks and IP addresses:

docker inspect frontend --format '{{range $name, $config := .NetworkSettings.Networks}}network={{$name}} ip={{$config.IPAddress}}{{println}}{{end}}'
docker inspect api --format '{{range $name, $config := .NetworkSettings.Networks}}network={{$name}} ip={{$config.IPAddress}}{{println}}{{end}}'

Expected output:

network=app-net ip=172.20.0.2
network=app-net ip=172.20.0.3

Then inspect the whole network:

docker network inspect app-net

Both names and their assigned addresses should appear under Containers.

PowerShell showing docker network inspect app-net with the Containers section, frontend and api names, and their IPv4 addresses visible

If only one container appears, attach the missing running container:

docker network connect app-net frontend

That manual connection is fine during an incident. For repeatable deployments, define network membership in Compose. You can also recreate the containers with --network app-net. Otherwise, the fault returns after the next rebuild, usually after everyone forgets the manual fix.

3. Pull and Start Netshoot

Minimal images often omit dig, nc, ss, ip, and tcpdump. Leave them that way. Installing packages inside a live production container changes the system you’re trying to inspect.

Pull a dedicated diagnostic image:

docker pull nicolaka/netshoot

Expected output ends with a digest or status line similar to:

Status: Downloaded newer image for nicolaka/netshoot:latest

Controlled setups should pin an approved immutable digest. The latest tag is handy in a lab, but it gives you no fixed version for later tests.

Join the application network:

docker run --rm -it --network app-net nicolaka/netshoot

Here, --rm removes the temporary container after exit. The -it flags open an interactive terminal. --network app-net attaches it to the application network.

Netshoot shell attached to app-net, showing the docker run command followed by nslookup api and nc -zv api 8080

Run these tests from the Netshoot shell:

ip route
nc -zv -w 3 172.20.0.3 8080
nslookup api
nc -zv -w 3 api 8080

The -z flag probes without sending application data. The -v flag prints the result. The -w 3 flag stops each attempt after three seconds.

A healthy result resembles:

Connection to 172.20.0.3 8080 port [tcp/*] succeeded!
Name: api
Address: 172.20.0.3
Connection to api 8080 port [tcp/*] succeeded!

The order saves time. If the IP works and the name fails, the route and service port already work. Focus on DNS. If both fail, DNS can wait.

Common Issues and Solutions

Problem: Two Containers Cannot Communicate by Name

Symptoms:

  • nslookup api returns no address.
  • The application reports Name or service not known.
  • Containers work through published host ports but not by container name.
  • Both containers were started without an explicit network.

Why it happens: Docker provides automatic container-name and alias lookup on user-defined networks. Name discovery works differently on the legacy default bridge network. The containers may also sit on separate networks. In that case, the failed lookup is expected isolation.

Fix:

Create a user-defined bridge:

docker network create app-net

Expected output is the new network ID:

908f628b842f1f9c5ae6c4f0931205108db175bc1c917d11adf495bb10396c50

Attach both running containers:

docker network connect app-net frontend
docker network connect app-net api

If Docker says the endpoint already exists, that container is already attached. Check the other container instead of repeating the same command.

Verification:

docker network inspect app-net
docker run --rm --network app-net nicolaka/netshoot nslookup api
docker run --rm --network app-net nicolaka/netshoot nc -zv -w 3 api 8080

The results should show the api address and a successful connection to port 8080.

Tip: Docker Compose usually creates a project-specific user-defined network. Use Compose service names between containers, not temporary IP addresses.

Problem: The Destination IP Works but Its Name Does Not

Symptoms:

  • nc -zv 172.20.0.3 8080 succeeds.
  • nc -zv api 8080 fails.
  • Applications report EAI_AGAIN, ENOTFOUND, or Temporary failure in name resolution.

Why it happens: A successful IP connection proves the route and application port work. The fault is now limited to the requested name, network scope, Docker’s embedded DNS, or the upstream resolver.

Fix:

Inspect the source container’s resolver configuration:

docker exec frontend cat /etc/resolv.conf

On a user-defined network, the output often includes Docker’s embedded resolver:

nameserver 127.0.0.11
options ndots:0

Search domains and ndots values vary. A different value proves little by itself, so don’t tune it before you collect timings.

PowerShell showing the source container resolv.conf with nameserver 127.0.0.11 and the resolver options highlighted without assuming a fixed ndots value

Compare an internal lookup with a public one from Netshoot:

docker run --rm --network app-net nicolaka/netshoot nslookup api
docker run --rm --network app-net nicolaka/netshoot nslookup example.com

Read the pair this way:

  • Internal fails, public succeeds: check the service name and shared network.
  • Internal succeeds, public fails: check upstream DNS.
  • Both fail: inspect resolver configuration and network attachment.
  • IP port succeeds while name lookup fails: leave the firewall alone for now.
Terminal showing a successful nc connection to the destination IP followed by a failed nslookup of the destination name, demonstrating DNS isolation

Verification:

docker run --rm --network app-net nicolaka/netshoot getent hosts api

Expected output:

172.20.0.3 api

Problem: Neither the Destination Name nor IP Works

Symptoms:

  • Name resolution fails.
  • Direct IP tests time out.
  • The source container has no clear route to the destination subnet.
  • The containers appear to be running.

Why it happens: The endpoints may use different networks. The destination address may also be stale. A missing default route or traffic filter can cause the same result.

Fix:

Get the current address instead of reusing one from yesterday’s container:

docker inspect api --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{println}}{{end}}'

Enter Netshoot on the shared network:

docker run --rm -it --network app-net nicolaka/netshoot

Inside the Netshoot shell:

ip address
ip route
nc -zv -w 3 172.20.0.3 8080

A normal route table includes the application subnet and a default route:

default via 172.20.0.1 dev eth0
172.20.0.0/16 dev eth0 proto kernel scope link src 172.20.0.4

If the application containers don’t share a network, fix membership first. Static routes between Docker networks add work and weaken isolation. Use them only when routed separation is part of the design.

Verification:

Retest the application port by IP, then by name:

docker run --rm --network app-net nicolaka/netshoot nc -zv -w 3 172.20.0.3 8080
docker run --rm --network app-net nicolaka/netshoot nc -zv -w 3 api 8080

Problem: The Name Resolves but the Connection Is Refused

Symptoms:

  • nslookup api returns the correct address.
  • nc returns Connection refused.
  • A host port may be published, but the application remains unavailable.

Why it happens: A refusal usually means the packet reached the destination network stack and got an immediate rejection. No process accepted that address and port. The service may be stopped, listening on another port, or bound only to 127.0.0.1.

Fix:

Share the destination container’s network namespace with Netshoot:

docker run --rm -it --network container:api nicolaka/netshoot

This mode doesn’t create another endpoint on app-net. Netshoot sees the target container’s interfaces, routes, and listening sockets.

Inside the shell:

ip route
ss -lntp

Expected healthy listener:

LISTEN 0 4096 0.0.0.0:8080 0.0.0.0:*

A problematic loopback-only listener looks like:

LISTEN 0 4096 127.0.0.1:8080 0.0.0.0:*

PowerShell showing Netshoot started with --network container:api, followed by ip route and ss -lntp with the default route and expected listener highlighted

Change the application’s bind address to 0.0.0.0 or the container interface address. The exact setting depends on the application. Common names include listen, host, and bind.

Verification:

docker run --rm --network app-net nicolaka/netshoot nc -zv -w 3 api 8080

A successful probe confirms DNS, routing, and the listener with one cheap test.

Problem: A Published Host Port Does Not Open the Application

Symptoms:

  • http://localhost:3001 fails.
  • docker ps shows an unexpected port mapping.
  • Docker reports that the host port is already allocated.
  • The service works between containers but not from the host.

Why it happens: Docker uses HOST_PORT:CONTAINER_PORT. Reversing the pair sends traffic to the wrong internal port. Another process may also own the host port. The image’s EXPOSE metadata only documents a port; it doesn’t publish one.

Fix:

Inspect publication:

docker port api

Expected output:

8080/tcp -> 0.0.0.0:3001
8080/tcp -> [::]:3001

Inspect the full binding if needed:

docker inspect api --format '{{json .HostConfig.PortBindings}}'

Then confirm the listener inside the container namespace:

docker run --rm --network container:api nicolaka/netshoot ss -lntp
Terminal showing docker port api followed by ss -lntp in the api network namespace, with host port 3001, container port 8080, and listener address visible

If the mapping is wrong, recreate the container:

docker stop api
docker rm api
docker run -d --name api --network app-net -p 3001:8080 example/api:2026

The -d flag runs the container in the background. The -p 3001:8080 option maps host port 3001 to container port 8080.

Warning: Recreating a container removes changes stored only in its writable layer. First confirm that important data lives in a named volume, bind mount, NAS drive, or external database. Containers are disposable; forgotten data often isn’t.

Verification:

From the host:

curl --connect-timeout 3 http://127.0.0.1:3001/

Any HTTP response proves the published path reaches the application. A 404 still means the network works. / may be the wrong application route.

Problem: Docker Reports “Port Is Already Allocated”

Symptoms:

  • Startup fails with Bind for 0.0.0.0:3001 failed: port is already allocated.
  • A different application appears at the expected address.
  • Recreating the container produces the same error.

Why it happens: Another container or host process already listens on the requested host port. Docker can’t share that binding just because both services have persuasive reasons.

Fix on Windows PowerShell:

Get-NetTCPConnection -LocalPort 3001 -State Listen

Then find the owning process:

Get-Process -Id (Get-NetTCPConnection -LocalPort 3001 -State Listen).OwningProcess

Fix on macOS:

lsof -nP -iTCP:3001 -sTCP:LISTEN

Also check Docker containers:

docker ps --filter publish=3001

Stop the conflicting service when that’s safe, or choose another host port:

docker run -d --name api --network app-net -p 3002:8080 example/api:2026

Verification:

docker port api
curl --connect-timeout 3 http://127.0.0.1:3002/

Problem: The Container Cannot Access the Internet by IP

Symptoms:

  • Public IP connections time out.
  • Public DNS also fails.
  • ip route lacks a default route.
  • Other host applications still have Internet access.

Why it happens: The container may have no valid gateway. On native Linux, disabled IPv4 forwarding can stop outbound traffic. Firewall policy or missing Network Address Translation rules can do the same.

Fix:

Probe a real application port on a public IP instead of relying on ping:

docker run --rm --network app-net nicolaka/netshoot nc -zv -w 5 1.1.1.1 443

Inspect routes:

docker run --rm --network app-net nicolaka/netshoot ip route

On a native Linux Docker host, check forwarding:

cat /proc/sys/net/ipv4/ip_forward

Expected output:

1

Inspect Docker’s user firewall chain and NAT rules:

sudo iptables -L DOCKER-USER -n -v
sudo iptables -t nat -L -n -v
Native Linux host terminal showing IPv4 forwarding set to 1, the DOCKER-USER chain, and relevant NAT entries after container-level tests have completed

Check for explicit drops in DOCKER-USER. Also check for missing Docker NAT rules or firewall software that replaced them.

Warning: Don’t flush iptables or reset the firewall on a remote host. Either action can expose services or cut off SSH. Change only a confirmed blocking rule, and keep console access ready.

Restarting the Docker daemon may rebuild its managed rules. It also interrupts workloads. Record the current state and treat a restart as a planned final step.

Verification:

docker run --rm --network app-net nicolaka/netshoot nc -zv -w 5 1.1.1.1 443
docker run --rm --network app-net nicolaka/netshoot nslookup example.com

Problem: External IPs Work but Domain Names Do Not

Symptoms:

  • nc -zv 1.1.1.1 443 succeeds.
  • nslookup example.com times out.
  • Applications report EAI_AGAIN or Temporary failure in name resolution.

Why it happens: Routing and NAT work. The configured upstream resolver is unavailable or doesn’t work with the host’s resolver setup.

Fix:

Capture the current resolver settings:

docker run --rm --network app-net nicolaka/netshoot cat /etc/resolv.conf

Test the resolver shown there:

docker run --rm --network app-net nicolaka/netshoot nslookup example.com

On native Linux, explicit daemon DNS settings can fix an unreachable host loopback resolver:

{
  "dns": ["1.1.1.1", "9.9.9.9"]
}

The native Linux daemon file is /etc/docker/daemon.json. Merge the dns key into the existing valid JSON. Don’t overwrite unrelated settings. That turns a DNS incident into a daemon configuration incident.

Warning: Restarting Docker can interrupt running containers. Validate the JSON and schedule a maintenance window before applying daemon-wide changes.

Docker Desktop stores daemon configuration in its managed environment. Editing the host’s /etc/docker/daemon.json doesn’t configure the Linux VM.

Verification:

Apply the setting through the supported Docker interface and recreate the affected container. Then test again:

docker run --rm --network app-net nicolaka/netshoot nslookup example.com

Problem: DNS Is Slow or Fails Intermittently

Symptoms:

  • Lookups alternate between success and EAI_AGAIN.
  • Short names are slower than fully qualified names.
  • Failures appear during resolver load or VPN changes.

Why it happens: Upstream timeouts can cause this. Search-domain expansion, a high ndots value, changing VPN resolvers, or mixed network membership can also cause it.

Fix:

Record the resolver file and repeat focused queries:

docker run --rm --network app-net nicolaka/netshoot cat /etc/resolv.conf
docker run --rm --network app-net nicolaka/netshoot dig api
docker run --rm --network app-net nicolaka/netshoot dig example.com

Run each lookup several times. Compare the query time, server, and status instead of trusting one successful response. Confirm network membership before changing resolver options.

If tests prove search expansion causes the delay, try an application-level setting such as ndots:1 outside production. A lower value cuts search-domain attempts, but it can change how short names resolve.

Verification:

Repeated internal and external lookups should return stable answers without timeouts. A daemon restart discards useful state and is a poor first response to intermittent DNS.

Problem: The Application Image Has No Diagnostic Tools

Symptoms:

  • docker exec api sh fails because no shell exists.
  • ping, curl, ss, or dig returns executable file not found.
  • The image is based on scratch, distroless, or another minimal base.

Why it happens: Production images often omit shells, package managers, and diagnostic tools. This cuts image size and attack surface. It’s a good trade until somebody tries docker exec.

Fix:

For tests from the same application network:

docker run --rm -it --network app-net nicolaka/netshoot

For the target container’s exact network namespace:

docker run --rm -it --network container:api nicolaka/netshoot

The first mode creates a separate diagnostic endpoint. The second shares the target’s interfaces, routes, and ports. That makes it better for listener and capture checks.

Verification:

docker run --rm --network container:api nicolaka/netshoot ip address
docker run --rm --network container:api nicolaka/netshoot ss -lntp

This method leaves the application image unchanged. The diagnostic container disappears when the command exits.

Problem: Requests Time Out Without a Useful Error

Symptoms:

  • nc waits and then times out.
  • The application produces no matching log entry.
  • DNS and network membership appear correct.
  • You can’t tell whether the destination receives packets.

Why it happens: Traffic may vanish in the source namespace, destination namespace, bridge, firewall, or return path. A timeout tells you the result, not the location.

Fix:

Start a filtered capture in the destination namespace:

docker run --rm -it --network container:api nicolaka/netshoot tcpdump -nn -i any tcp port 8080

The flags mean:

  • -nn: don’t resolve hostnames or service names
  • -i any: capture on all visible interfaces
  • tcp port 8080: limit output to relevant TCP traffic

In another terminal, generate one test:

docker run --rm --network app-net nicolaka/netshoot nc -zv -w 3 api 8080

A connection attempt may show:

172.20.0.4.43162 > 172.20.0.3.8080: Flags [S], seq 1201971, win 64240, length 0
172.20.0.3.8080 > 172.20.0.4.43162: Flags [S.], seq 442100, ack 1201972, length 0

The first packet is a TCP SYN. The second is a SYN-ACK reply. A SYN with no reply points to the listener, destination filter, or return route.

Terminal showing tcpdump filtered to TCP port 8080 with a connection attempt, source and destination addresses, SYN packet, and whether a reply appears

Move the capture point outward:

  • Source namespace
  • Destination namespace
  • Native Linux host bridge, if applicable
  • External interface, only when diagnosing outbound traffic

The first place where packets disappear identifies the layer to inspect. This takes longer than guessing, but it limits changes to the broken layer.

Verification:

After fixing the listener, route, or rule, repeat the same capture. A complete TCP handshake shows SYN, SYN-ACK, and ACK packets.

Error Messages Quick Reference

Error messageMeaningFirst check
Name or service not knownThe requested name could not be resolvedShared user-defined network and exact service name
Temporary failure in name resolutionThe resolver timed out or was unavailable/etc/resolv.conf and internal versus public lookups
EAI_AGAINTemporary DNS lookup failureUpstream DNS timing and repeated dig tests
ENOTFOUNDThe application received no usable DNS resultRequested name, aliases, and network scope
Connection refusedThe destination replied, but no listener accepted the portss -lntp in the destination namespace
Connection timed outNo usable response returned before the deadlineRoute, firewall, listener, and tcpdump
No route to hostThe namespace lacks a route or received an unreachable responseip route and network membership
Network is unreachableNo route matches the destinationDefault route and Docker endpoint configuration
port is already allocatedAnother container or host process owns the host portdocker ps --filter publish=PORT and host listener tools
endpoint with name ... already existsThe container is already attached to that networkdocker inspect before reconnecting
network ... not foundThe named network does not existdocker network ls
executable file not foundThe application image lacks the requested toolRun nicolaka/netshoot
permission denied from packet captureThe process lacks capture capabilityUse Netshoot with appropriate Docker privileges in an approved environment
address already in useA process already owns the requested bind address and portIn-container and host listener checks

Platform-Specific Fixes

Windows

Docker Desktop’s Linux container networks run inside a managed VM. Windows PowerShell can inspect published host ports. Native Windows firewall commands can’t show the VM’s internal Docker bridge rules.

Confirm Docker is reachable:

docker version
docker info

Check a published listener:

Get-NetTCPConnection -LocalPort 3001 -State Listen
Test-NetConnection -ComputerName 127.0.0.1 -Port 3001

Expected successful test:

ComputerName : 127.0.0.1
RemotePort : 3001
TcpTestSucceeded : True

Windows desktop with PowerShell open, Docker Desktop running, and Test-NetConnection showing a successful localhost port test

If containers can reach each other but Windows can’t reach the published port, check:

  • docker port api
  • The listener inside api
  • A conflicting Windows process
  • Windows firewall or endpoint security
  • VPN software that changes routes or DNS

Don’t search Windows for the Linux VM’s DOCKER-USER chain. Inspect Linux firewall and NAT state only on a native Linux host. You can also use supported access to the Docker Desktop VM.

macOS

Docker Desktop also runs Linux containers inside a managed VM on macOS. Host tools can test published ports. macOS can’t directly show the VM’s Linux bridge or Docker iptables chains.

Confirm Docker and test the host port:

docker version
docker port api
lsof -nP -iTCP:3001 -sTCP:LISTEN
curl --connect-timeout 3 http://127.0.0.1:3001/
macOS desktop with Terminal open, Docker Desktop running, and docker port plus lsof output showing the published localhost port

When networking fails after a VPN connects, test these paths separately:

  • Container-to-container traffic by IP
  • Container-name resolution
  • Public IP connectivity
  • Public DNS resolution
  • Host access to a published port

Those five results show whether the VPN changed host DNS or routes. They also show whether the fault remains inside the application network.

Don’t rely on an unverified Docker Desktop menu path. The interface moves between releases. Use the current settings search or the relevant Docker Desktop release notes for documented VM, DNS, or network options.

Advanced Linux Namespace Inspection with nsenter

On a native Linux host, nsenter can open an existing process’s network namespace. It’s useful when Netshoot can’t be pulled or when you need host-installed tools.

It is lower-level than:

docker run --rm -it --network container:api nicolaka/netshoot

nsenter needs elevated host access, the correct process ID, and compatible host tools. It also works close to production processes. The wrong namespace can give you convincing output from the wrong place. That’s worse than no output.

Exact commands depend on the installed Docker and util-linux versions. Check your distribution’s current documentation before using namespace commands. For most incidents, Netshoot with --network container:api gives you the needed view with less host coupling.

Configuration Issues to Check

Default Bridge Used for Name-Based Discovery

These commands start containers on Docker’s default bridge:

docker run -d --name api example/api:2026
docker run -d --name frontend example/frontend:2026

Automatic name lookup between those containers won’t match a user-defined bridge.

Use a named network:

docker network create app-net
docker run -d --name api --network app-net example/api:2026
docker run -d --name frontend --network app-net example/frontend:2026

User-defined bridges give you clearer isolation and easier lifecycle management. The trade-off is explicit network membership. Keep that setting in Compose or other deployment configuration.

Reversed Port Mapping

This mapping is wrong when the application listens on 8080 and the desired host port is 3001:

docker run -d -p 8080:3001 example/api:2026

Use:

docker run -d -p 3001:8080 example/api:2026

The host port is on the left. The container port is on the right. Write mappings as host:container in runbooks. It prevents a common five-minute argument.

Service Bound Only to Loopback

A process listening on 127.0.0.1:8080 accepts connections only from its own network namespace. Docker port publication can’t force that process to accept traffic sent to the container interface.

Configure the service to listen on:

0.0.0.0:8080

Use the application’s supported bind setting. Changing routes won’t fix a loopback-only listener.

EXPOSE Mistaken for Port Publication

This Dockerfile instruction documents the intended port:

EXPOSE 8080

The instruction doesn’t publish anything to the host. Publish the port when you create the container:

docker run -d -p 3001:8080 example/api:2026

Hard-Coded Container IP Addresses

Container addresses can change after recreation. Use service names and network aliases on a user-defined network:

docker run -d \
  --name api \
  --network app-net \
  --network-alias backend \
  example/api:2026

Other containers on app-net can reach the service as api or backend. Names survive routine recreation. An address such as 172.20.0.3 usually doesn’t.

Overlapping Subnets

A Docker network may overlap with a VPN, office LAN, NAS, or homelab VLAN. Traffic can then go toward the wrong gateway. These faults often appear only when the VPN connects, which makes them pleasantly inconsistent.

Inspect the subnet:

docker network inspect app-net --format '{{json .IPAM.Config}}'

Expected output resembles:

[{“Subnet”:”172.20.0.0/16″,”Gateway”:”172.20.0.1″}]

Compare that subnet with the host and VPN routes. If they overlap, create a network with an unused private range. Then recreate the containers on it. Pick ranges during network planning, not during an outage with three terminals open.

Getting Help

Collect evidence before opening an issue. Remove secrets, private image names, public IP addresses, and sensitive DNS search domains before posting output.

Docker and Container Diagnostics

docker version
docker info
docker ps --all
docker network ls
docker network inspect app-net
docker inspect frontend
docker inspect api
docker logs --tail 200 api

Record focused network output:

docker inspect api --format '{{json .NetworkSettings.Networks}}'
docker port api
docker run --rm --network app-net nicolaka/netshoot ip route
docker run --rm --network app-net nicolaka/netshoot nslookup api
docker run --rm --network app-net nicolaka/netshoot nc -zv -w 3 api 8080

The full docker inspect output is useful, but it’s large and may contain environment variables. Review it before attaching it to a public issue.

Log Locations

Useful sources include:

  • docker logs CONTAINER for application standard output and errors
  • Docker daemon logs on native Linux, often available through the system journal
  • Docker Desktop diagnostics on Windows and macOS
  • Application logs stored in configured volumes
  • A short, filtered tcpdump capture for unexplained timeouts

On a native Linux host using systemd:

sudo journalctl -u docker.service --since "30 minutes ago"

Review packet captures before sharing them. They can contain internal addresses, DNS queries, session data, and clear-text application payloads.

Where to Ask

Use:

  • Docker documentation
  • Docker Community Forums
  • The issue tracker for the affected application image
  • Your organization’s network or security team when DOCKER-USER, VPN, endpoint security, or upstream firewall policy is involved

A useful report names the source, destination, network driver, expected port, exact error, and first failing layer. State whether the problem occurs without the VPN. “Docker networking is broken” gives everyone very little to work with.

Prevention Tips

  • Use user-defined networks: Put each application stack on a named bridge so Docker-managed service discovery works.
  • Define networks declaratively: Keep network membership, aliases, and published ports in Compose or deployment configuration.
  • Use service names: Don’t treat a container IP address as stable configuration.
  • Add health checks: A running container may have an application that isn’t ready. Test the real endpoint.
  • Document port direction: Record mappings as host:container, such as 3001:8080.
  • Bind services correctly: Containerized servers usually need 0.0.0.0, rather than only 127.0.0.1.
  • Keep application images minimal: Use Netshoot as a temporary diagnostic container instead of installing tools.
  • Pin diagnostic images: Approve and pin a Netshoot digest in controlled setups.
  • Plan Docker subnets: Avoid overlaps with office LANs, VPN routes, NAS networks, and homelab VLANs.
  • Preserve firewall policy: Manage DOCKER-USER rules through configuration and test them after firewall upgrades.
  • Monitor infrastructure power: A UPS can prevent sudden host and network-device shutdowns that cause confusing recovery faults.
  • Record a healthy baseline: Save expected network names, subnets, routes, listeners, and port mappings before an incident.
  • Review release notes: Check Docker Desktop changes before blaming application configuration for a new fault.

Frequently Asked Questions

Why can containers communicate by IP but not by name?

The packet path works, but name resolution fails. Confirm both containers share the same user-defined network. Check the exact service name and inspect /etc/resolv.conf for Docker’s embedded resolver.

Why does the default bridge behave differently?

The default bridge is a legacy shared network. User-defined bridges provide Docker-managed DNS for container names and aliases. They also give you clearer isolation and configuration. Use a named network for multi-container applications.

Should I restart Docker when DNS fails?

Keep the restart for later. It can interrupt containers and erase useful incident state. First test network membership, IP access, resolver configuration, internal names, and public names.

When should I use tcpdump?

Use it when status, membership, IP, DNS, route, and listener checks can’t explain a timeout. Packet capture shows whether traffic reaches a namespace and whether a reply leaves it.

When should I share the target container’s network namespace?

Use --network container:api for the target’s exact routes, interfaces, listeners, or packet view. Use --network app-net to test as another peer on the application network.

Wrapping Up

StepActionApplies To
1Confirm source and destination are runningAll platforms
2Inspect shared network membershipAll platforms
3Test the application port by current IPAll platforms
4Test Docker name resolutionAll platforms
5Inspect the destination listener and routeAll platforms
6Verify host port publication and conflictsWindows and macOS
7Capture packets at the first uncertain layerAll platforms
8Inspect forwarding, firewall, and NATNative Linux hosts

Find the first broken layer and change only that layer. Shared-network mistakes and loopback-only listeners cause more trouble than Docker itself. Netshoot and a three-second nc probe usually expose either fault within a few minutes.

Last updated: 2026-09-06 | Applies to Docker Engine container networking through Docker Desktop on Windows and macOS, with native Linux host checks where noted