Problem overview
You see an error like:
- network_mode: host is incompatible with port bindings
- ports are not supported with network_mode: host
Root cause: host networking shares the host’s network namespace, so Docker cannot perform port mapping. In Compose, you must either:
- Use network_mode: host and remove ports, or
- Use the default bridge network and keep ports.
This guide shows both fixes with minimal examples, when to choose each, and common pitfalls.
When to use each option
| Need | Recommended |
|---|---|
| Maximum network performance, minimal overhead, or raw access to host interfaces | network_mode: host (no ports) |
| Port mapping, isolation, portability across machines and OSes | Default bridge network with ports |
Note: host networking is only fully supported on Linux. On macOS/Windows, prefer bridge + ports.
Quickstart: choose your fix
- If you want host networking:
- Remove the ports section from the service.
- Ensure your app listens on the correct host port (e.g., 8000).
- Start the stack and access via localhost:PORT.
- If you want port bindings:
- Remove network_mode: host.
- Keep or add ports: "HOST:CONTAINER" mappings.
- Start the stack and access via localhost:HOST_PORT.
Minimal working examples
A) Host networking (no ports)
Use this on Linux when you need direct host networking.
type: docker-compose
services:
web:
image: python:3.12-alpine
command: ["python", "-m", "http.server", "8000"]
network_mode: host
# IMPORTANT: do not declare `ports` here
Run:
docker compose up --quiet-pull
Test:
curl -I http://127.0.0.1:8000/
Explanation: The container shares the host stack. The Python server binds to 8000 on the host directly. No -p/ports is allowed or needed.
B) Bridge networking (with ports)
Use this if you need port mapping or cross‑platform behavior.
type: docker-compose
services:
web:
image: python:3.12-alpine
command: ["python", "-m", "http.server", "8000"]
ports:
- "8000:8000" # HOST:CONTAINER
Run and test:
docker compose up --quiet-pull
curl -I http://127.0.0.1:8000/
Explanation: Docker publishes host port 8000 and forwards traffic to container port 8000 on the bridge network.
Step-by-step fixes
- Identify the intent
- Do you need direct access to host NICs/iptables, or ultra-low latency? Choose host networking.
- Do you need port mappings and service isolation? Choose bridge.
- Edit compose.yaml
- Host networking: add network_mode: host and remove any ports entries for that service.
- Bridge networking: remove network_mode, configure ports: ["HOST:CONTAINER"].
- Validate configuration
- docker compose config to confirm the final spec.
- Restart cleanly
- docker compose down --remove-orphans
- docker compose up -d
- Test
- Host networking: curl http://127.0.0.1:<app_port>
- Bridge networking: curl http://127.0.0.1:<host_port>
Platform notes
- Linux: host networking works as described.
- macOS/Windows (Docker Desktop): host networking for Linux containers is not supported the same way. Prefer bridge + ports. To access services on the host from a container, use the special hostname host.docker.internal.
Common pitfalls
- Mixing ports with host mode: Any ports: section on a service with network_mode: host will fail. Remove it.
- Port collisions in host mode: Two services cannot listen on the same host port. Change the app’s listen port per instance.
- App bind address:
- Bridge mode: ensure the app listens on 0.0.0.0 inside the container, not only 127.0.0.1, otherwise Docker’s published port may not reach it.
- Host mode: the container shares the host namespace; binding to 127.0.0.1 limits access to the host loopback only (often fine), but won’t expose to LAN.
- Cross‑platform differences: host mode behavior is Linux‑specific; avoid it if you need portability.
- Health checks and firewalls: In host mode, host firewalls apply directly to the app. In bridge mode, traffic goes through Docker’s NAT rules.
- Swarm deploys: network_mode is not supported in swarm services via compose deploy. Use bridge/overlay networks and published ports for swarm.
Performance notes
- Host networking removes NAT and virtual bridge hops. This can lower latency and CPU overhead for high‑packet‑rate workloads (e.g., DNS, proxies, load balancers).
- Bridge mode overhead is typically small for most web apps and APIs. The benefits of isolation, portability, and flexibility often outweigh the minimal cost.
- Throughput bottlenecks usually lie in the application, crypto, or kernel TCP settings rather than the Docker bridge itself. Measure before optimizing.
Troubleshooting checklist
- I need host networking:
- [ ] Remove ports from the service.
- [ ] Confirm the app listens on the desired host port.
- [ ] Check for port collisions with lsof -i :PORT.
- I need port mappings:
- [ ] Remove network_mode: host.
- [ ] Set ports: HOST:CONTAINER and ensure the app listens on 0.0.0.0:CONTAINER.
- [ ] Verify docker compose ps shows the published port.
Tiny FAQ
Why can’t I use ports with network_mode: host? Because Docker can’t map ports when the container already shares the host network namespace; there’s nothing to translate.
Can I run multiple instances with host mode? Yes, but each must listen on a unique host port. Docker cannot remap them for you.
Is host networking faster? Sometimes, especially for UDP or packet‑heavy workloads. For typical web apps, the bridge overhead is minimal.
What about macOS/Windows? Prefer bridge + ports. host.docker.internal lets containers reach services on the host.
How do I expose multiple ports in bridge mode? Add more entries under ports, e.g., "80:80", "443:443".
Summary
- You cannot combine network_mode: host with ports in Docker Compose.
- Choose one:
- Host networking: network_mode: host and no ports.
- Bridge networking: remove network_mode and use ports.
- Use host networking mainly on Linux for specialized performance or network access needs; otherwise prefer bridge for portability and simplicity.