Overview
The error “Error response from daemon: driver failed programming external connectivity” appears when Docker cannot publish a port on the host. Most cases boil down to a host port conflict, firewall/iptables rules blocking NAT, or OS virtualization quirks (WSL2, VPNs, Hyper-V).
This guide shows quick checks, step-by-step fixes, and OS-specific notes for Linux, Windows, macOS, and WSL2.
Quickstart
- Check if the host port is busy.
- Linux/macOS:
sudo lsof -iTCP:8080 -sTCP:LISTEN -nP || true
ss -tulpn | grep :8080 || true
- Windows (PowerShell):
netstat -ano | findstr :8080
Get-Process -Id (Get-NetTCPConnection -LocalPort 8080).OwningProcess
If something is listening, stop it or pick another host port.
- Restart Docker networking.
# Linux
sudo systemctl restart docker
# macOS/Windows: restart Docker Desktop from the UI or CLI
- If still failing, follow the steps below by OS.
Minimal working example
Use this to validate your host can publish a port. If this fails, the issue is your host network/firewall, not the app.
Dockerfile:
FROM python:3.12-alpine
WORKDIR /app
RUN echo "OK" > index.html
EXPOSE 8000
CMD ["python", "-m", "http.server", "8000"]
Build and run:
docker build -t port-test .
docker run --rm -p 8080:8000 port-test
If it starts, visit http://localhost:8080. If you get the error, proceed to troubleshooting.
Common causes
- Port conflict: host port already bound by another process or container.
- Firewall/iptables/nftables policies block Docker’s NAT rules.
- IP forwarding disabled on the host kernel.
- VPN or security software intercepts network (especially on Windows/macOS/WSL2).
- Outdated or mismatched iptables backend (legacy vs nft) on Linux.
- Duplicate mappings in Compose or conflicting network modes.
- SELinux/AppArmor/antivirus interfering with low ports or networking.
Step-by-step fixes (Linux)
- Free or change the host port.
# Find listener
sudo lsof -iTCP:8080 -sTCP:LISTEN -nP
# Stop service (example)
sudo systemctl stop nginx || true
# Or change mapping, e.g. use -p 8081:8000 instead of 8080:8000
- Verify Docker’s iptables rules are present.
sudo iptables -S DOCKER 2>/dev/null || echo "DOCKER chain missing"
sudo iptables -S DOCKER-USER 2>/dev/null || true
If chains are missing or NAT rules seem broken:
sudo systemctl restart docker
- Ensure kernel IP forwarding is enabled.
sudo sysctl -w net.ipv4.ip_forward=1
sudo sysctl -w net.ipv6.conf.all.forwarding=1
Persist by adding to /etc/sysctl.d/99-docker.conf and reloading sysctl.
- Align iptables backend (legacy vs nft) if networking is flaky.
- On Debian/Ubuntu, choose one backend consistently:
# Option A: use nft (preferred on modern systems)
sudo update-alternatives --set iptables /usr/sbin/iptables-nft
sudo update-alternatives --set ip6tables /usr/sbin/ip6tables-nft
# Option B: use legacy (helps on older Docker/kernel combos)
# sudo update-alternatives --set iptables /usr/sbin/iptables-legacy
# sudo update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy
sudo systemctl restart docker
- Review firewall managers (UFW/firewalld) and allow forwarding.
- UFW quick allow for a test port:
sudo ufw allow 8080/tcp
sudo ufw reload
- Firewalld quick allow:
sudo firewall-cmd --permanent --add-port=8080/tcp
sudo firewall-cmd --reload
If forwarding is blocked, ensure FORWARD policy is ACCEPT or add explicit forward rules. As a last resort for testing only:
sudo iptables -I DOCKER-USER -j ACCEPT
Revert by deleting the rule when done.
- Reset Docker networks if corrupted.
docker network ls
docker network inspect bridge | head
# Prune unused networks (careful: removes unused)
docker network prune -f
sudo systemctl restart docker
- Rootless Docker specifics
- Low ports (<1024) need special privileges; prefer high ports (e.g., 8080).
- Ensure slirp4netns/iptables rules are set up by the rootless setup script.
Windows and WSL2 notes
- Check port conflicts on Windows host even if the container runs in WSL2.
netstat -ano | findstr :8080
Stop-Process -Id <PID>
- Restart Docker Desktop and WSL2:
wsl --shutdown
- VPNs/security suites may capture ports; temporarily disconnect/disable to test.
- For WSL2, avoid publishing to privileged ports and prefer localhost bindings.
- In Compose, explicitly bind to 127.0.0.1 if corporate policies restrict 0.0.0.0.
macOS notes
- Use Activity Monitor or lsof to find port conflicts.
sudo lsof -iTCP:8080 -sTCP:LISTEN
- VPN/security tools can block port proxies; test with them disabled.
- Restart Docker Desktop to rebuild the NAT.
Compose-specific checks
- Avoid duplicate host ports across services on the same project.
- Ensure only one network mode is used (do not mix host_network with ports).
- Minimal docker-compose.yml test:
version: "3.8"
services:
web:
build: .
ports:
- "8080:8000"
Start with:
docker compose up --build
If it fails, check for another service already using 8080.
Performance notes
- Port publishing uses NAT; many rules can slow container start slightly.
- Disabling userland proxy can reduce overhead:
{
"userland-proxy": false
}
Place in /etc/docker/daemon.json (Linux) or Docker Desktop settings, then restart Docker. Validate your environment, as some setups rely on the proxy.
Pitfalls to avoid
- EXPOSE in Dockerfile does not publish a host port; you still need -p.
- Binding to a low host port (e.g., 80) often conflicts with system services.
- Multiple compose projects on the same host can collide on ports.
- Aggressive firewalls may silently drop NAT; always test with a known-good minimal container.
Tiny FAQ
What does the error actually mean?
- Docker failed to add the necessary NAT/forwarding rules for your requested -p mapping.
How do I find what’s using my port?
- Linux/macOS: lsof or ss. Windows: netstat/Get-NetTCPConnection.
Do I need to open the port in the firewall?
- Yes, if inbound traffic must reach the host from other machines. For localhost-only testing, firewall rules are often unnecessary.
Compose still fails after I freed the port. Why?
- Another service in the same project or on the host may reuse it; also check VPNs, WSL2, and iptables backend.
Can SELinux block this?
- Yes. Test by setting a permissive mode temporarily to diagnose, then add proper policies or use allowed ports.
Summary
Start with port conflicts, then verify Docker’s iptables/NAT and IP forwarding. Restart Docker, align iptables backend, adjust firewall, and account for VPN/WSL2 quirks. Use the minimal example to validate fixes.