Troubleshooting Docker Port Mapping Issues: Why Containers Are Not Accessible Externally


3 views

Recently while working with Docker on SUSE Linux Enterprise Server 12 SP1, I encountered a puzzling scenario where containers were accessible locally but unreachable from external networks. Let me walk through the complete diagnostic process and solution.

First, I launched a basic nginx container:

docker run -d -p 8081:80 nginx:alpine --name nginxtest

Local access worked perfectly:

curl http://localhost:8081
<!-- [...] -->
<title>Welcome to nginx!</title>
<!-- [...] -->

But external access failed when trying to reach http://server-ip:8081 from another machine.

The key network interfaces showed:

docker0   Link encap:Ethernet  HWaddr 02:49:68:4D:40:9B
          inet addr:172.17.0.1  Bcast:0.0.0.0  Mask:255.255.0.0

Bridge configuration appeared normal:

bridge name     bridge id               STP enabled     interfaces
docker0         8000.0249143a607f       no              veth0e4b1f4
                                                        veth1f2fcc2

The NAT table revealed potential issues:

Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination
MASQUERADE  all  --  172.17.0.0/16        0.0.0.0/0
MASQUERADE  tcp  --  172.17.0.2           172.17.0.2           tcp dpt:80

While the port mapping was configured in Docker:

0.0.0.0:8081->80/tcp

The key issue stemmed from the net.ipv4.ip_forward = 0 setting in /etc/sysctl.conf combined with network service restarts. After running wicked service restart, the IP forwarding got disabled.

First, enable IP forwarding permanently:

echo "net.ipv4.ip_forward = 1" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p

Then verify the Docker network configuration:

docker network inspect bridge | grep -i masquerade

For immediate testing, you can add explicit iptables rules:

sudo iptables -A FORWARD -i eth0 -o docker0 -j ACCEPT
sudo iptables -A FORWARD -i docker0 -o eth0 -j ACCEPT

If the issue persists, consider these additional steps:

# Check for conflicting services
sudo netstat -tulnp | grep 8081

# Try host network mode for testing
docker run --network host -d nginx:alpine

For production environments, I recommend creating custom bridge networks:

docker network create --driver bridge my_custom_net
docker run -d --network my_custom_net -p 8081:80 nginx:alpine

After applying the fix, verify with:

cat /proc/sys/net/ipv4/ip_forward  # Should return 1
sudo iptables -L -n -v | grep FORWARD
curl -I http://your-server-ip:8081

I recently encountered a perplexing scenario where my Docker containers responded to local requests but remained inaccessible from external networks. Here's what I observed:

# Local access works
curl http://localhost:8081
<title>Welcome to nginx!</title>

# External access fails (timeout)
http://10.etc.etc.etc:8081

The problem emerged after running systemctl restart wicked, suggesting network configuration changes. Let's examine the current state:

# Bridge network status
ifconfig docker0
docker0   Link encap:Ethernet  HWaddr 02:49:68:4D:40:9B
          inet addr:172.17.0.1  Bcast:0.0.0.0  Mask:255.255.0.0

# Container port mapping
docker ps
0.0.0.0:8081->80/tcp

The iptables configuration reveals potential forwarding issues:

iptables -nt nat -L
Chain DOCKER (2 references)
DNAT       tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:8081 to:172.17.0.2:80

Notice the MASQUERADE rules for other ports but potential missing elements for our case.

These commands help verify connectivity at different levels:

# Check port listening status
ss -tulnp | grep 8081

# Test local container access
docker exec -it nginxtest wget -qO- localhost:80

# Verify external interface accessibility
ip route get 10.etc.etc.etc

After thorough investigation, here's the step-by-step resolution:

# 1. Ensure proper IP forwarding
echo 1 > /proc/sys/net/ipv4/ip_forward

# 2. Update iptables rules
iptables -I FORWARD 1 -i eth0 -o docker0 -j ACCEPT
iptables -I FORWARD 1 -i docker0 -o eth0 -j ACCEPT

# 3. Verify Docker network binding
docker network inspect bridge | grep host_binding_ipv4

# 4. Restart Docker service with clean networking
systemctl restart docker

# 5. For persistent changes, update sysctl.conf
echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf
sysctl -p

If standard port mapping fails, consider these alternatives:

# Use host networking mode
docker run --network host nginx:alpine

# Or expose ports to specific interface
docker run -d -p 10.etc.etc.etc:8081:80 nginx:alpine

# For production environments, use Docker Swarm or Kubernetes:
docker service create --publish published=8081,target=80 nginx:alpine
  • Always document network changes
  • Use version-controlled Docker Compose files for port mappings
  • Implement health checks for critical services
  • Consider using --publish-all flag during development
# Example health check in Dockerfile
HEALTHCHECK --interval=30s --timeout=3s \
  CMD curl -f http://localhost:80/ || exit 1