skip to content

On Docker Desktop, why doesn't `--network host` put a container on the Mac's network?

level: middleimportance: must knowfreq 56%

answer

  1. Ask which machine the word host names
  2. The namespace being shared is not yours
  3. The listener really opened, just elsewhere
  4. Container subnets exist on the far side
  5. Publishing is the supported crossing

basics

~20 s

"The host" under Docker Desktop means its Linux VM, not macOS. --network host joins the VM's network namespace and binds the VM's interfaces, and macOS has no route in. Publish with -p instead, which Desktop forwards inward.

solid answer

~50 s

On a Linux machine, `--network host` puts the container in the host's own network namespace: a process that binds port 8080 is instantly reachable on the machine's addresses, with no publishing and no NAT. Under Docker Desktop the daemon and its containers live inside a Linux VM, so the network namespace being shared is the VM's. The service really does bind — inside the VM — and your Mac, which is a separate machine as far as the network stack is concerned, has no interface or route to it. The same reasoning explains why `curl 172.17.0.4:8080` from a macOS terminal never connects: that address exists on a bridge inside the VM. The supported path on Desktop is to publish (`-p 8080:8080`), which makes Desktop listen on the host side and forward the connection into the VM; the reverse direction, container to laptop, is `host.docker.internal`.

code

bash · 7 lines
bash
# Reachable from the Mac: Desktop forwards the port into the VM
docker run -d --name digest -p 8493:8493 digest-builder:1.4
curl -s http://localhost:8493/health

# Bound inside the VM only; nothing on macOS is listening
docker run -d --name digest-host --network host digest-builder:1.4
curl -s --max-time 3 http://localhost:8493/health || echo 'no route from macOS into the VM'

go deeper

for a junior

Remember the practical rule: on Docker Desktop you reach a container through a published port and http://localhost:<port>, not through the IP address that docker inspect prints.

for a middle

Be able to explain the mechanism — host networking shares a network namespace, and the namespace being shared belongs to Desktop's Linux VM — and to name the two supported crossings, published ports inward and host.docker.internal outward.

for a senior

Show you can diagnose this quickly under pressure: prove the listener exists by curling from a second container in the same namespace, then distinguish a boundary problem from an application binding to loopback, and keep Desktop-only names out of shipped configuration.

for a principal

Own the parity question: decide where developer-laptop networking is allowed to differ from production, and make sure the difference is documented and tested in CI on Linux rather than discovered during an incident.

### What `--network host` means, and where the word "host" points Every container normally gets its own network namespace: its own interfaces, routing table, iptables rules and port space. `--network host` says the opposite — do not create a namespace, put this process straight into the host's. The visible effect on a Linux server is that publishing becomes meaningless and unnecessary: a process that binds `0.0.0.0:8080` is on the machine's real addresses immediately, and `-p` is rejected as pointless. On Docker Desktop the phrase still means exactly that, but "the host" is Docker Desktop's Linux VM, because that is where `dockerd` and the container process live. Your Mac or Windows desktop is a *different* machine on the other side of a virtual boundary. So the container joins the VM's network namespace and binds on the VM's interfaces, and the socket really is open — inside the VM. From your laptop's terminal there is simply no route there, so the connection is refused or hangs, and people conclude that `--network host` is "broken" when it did precisely what it says. ### Why container IPs behave the same way `docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' web` will happily print something like `172.17.0.4`, and on a Linux host you can `curl` that address directly because the bridge interface is attached to that host's stack. Under Desktop the bridge is inside the VM. Your host has no interface on that subnet and no route to it. This trips people up because the value *looks* like a normal LAN address; nothing in the output hints that it is meaningful only on the far side of a VM boundary. ### The two supported crossings **Inward: publish a port.** `docker run -p 8080:8080` makes Desktop listen on the host side, on macOS or Windows, and forward accepted connections into the VM to the container's port. That is why `http://localhost:8080` works in your browser on Desktop even though nothing on macOS is running the server. Bind the container process to `0.0.0.0` rather than `127.0.0.1` inside the container, or the forwarded connection arrives at a port nobody is listening on and you get a connection reset that looks like a Docker fault. **Outward: `host.docker.internal`.** A container that must reach a service running on your laptop — a database you started natively, an IDE debug listener — resolves `host.docker.internal` to an address that routes back out of the VM to the host. `gateway.docker.internal` names the VM's gateway. Neither name exists on a plain Linux engine, so anything that depends on them is Desktop-specific configuration and must not leak into a production Compose file or image. ### A worked example A team ships a C++ email-digest builder as a daemon that links several runtime shared libraries. The daemon binds `0.0.0.0:8493` and is started on a developer Mac with `--network host` because that is what the production Linux runbook says. It logs "listening on 8493" and the health check on the laptop returns connection refused every time. The developer spends a morning suspecting the C++ service's socket setup. The real explanation is one line long: the process is listening on port 8493 *of the VM*. `docker run --rm --network host curlimages/curl -s localhost:8493/health` succeeds, because that curl container is also in the VM's network namespace. The fix on Desktop is `-p 8493:8493` and `http://localhost:8493/health` from the Mac. ### The version wrinkle worth naming Recent Docker Desktop releases (4.34 and later) added an opt-in host-networking capability for Mac and Windows that makes `--network host` bind on the host loopback as well. It is a setting you must turn on, not the historical default, so the safe interview answer is: by default and for years, host networking on Desktop meant the VM's network; check the Desktop version and its network settings before assuming otherwise. Note also that even where it is enabled, this is a Desktop convenience — Compose files and deployment manifests that assume host networking still behave the Linux way in production. ### The general rule When a networking behaviour on Desktop surprises you, replace the word "host" with "the Linux VM" in whatever documentation you are reading and re-read the sentence. Most of the surprises disappear.

  • A container on Docker Desktop must reach a Postgres instance you started natively on your Mac. What address do you give it?
    `host.docker.internal`, which Desktop resolves to an address that routes out of the VM back to the host. `localhost` inside the container is the container itself, and the laptop's LAN address may work but changes with the network you are on. Keep the name out of anything that ships: it does not exist on a plain Linux engine, so inject it as configuration for local development only.
  • The port is published and the container is running, but connections from the Mac are refused. What do you check first?
    What the process bound inside the container. If it binds `127.0.0.1`, the forwarded connection arrives from outside the container's loopback and is refused; it must bind `0.0.0.0`. Confirm with `docker exec` and a socket listing inside the container, and check `docker port <name>` to see the mapping the engine actually created rather than what you believe you typed.
  • Does the same reasoning apply to the WSL 2 backend on Windows?
    Yes, with a twist. The engine runs in a Docker-managed WSL 2 distribution, so container IPs live inside that VM and are not addresses on your Windows network; published ports are forwarded from Windows into it, and `localhost` on Windows reaches them. Other WSL distributions integrated with Desktop share the same engine, so from a Linux shell there you are already on the VM side of the boundary.

saying these in an interview costs you the question

  • Says host networking is broken on macOS
  • Expects to curl 172.17.x.x from the Mac
  • Thinks --network host means the laptop's interfaces
  • Uses localhost inside a container to reach the laptop
  • Blames the application's socket binding first
  • Assumes Desktop behaviour proves Linux behaviour

context