host.docker.internal is a special DNS hostname that lets Docker containers reach services running on the host machine. On macOS and Windows, Docker Desktop resolves it automatically. Linux requires an explicit mapping through the extra_hosts directive or the --add-host flag — both introduced to the host-gateway feature in Docker Engine 20.10.
Understanding host.docker.internal and How It Resolves
When a container sends traffic to host.docker.internal, Docker resolves the hostname to the gateway IP of the bridge network — typically 172.17.0.1 on default installations. That address is the Docker host from the container’s perspective.
Mac and Windows run containers inside a lightweight VM, so Docker Desktop can inject this DNS entry automatically. Linux runs containers directly on the kernel without that VM layer, which is why the mapping needs to be declared manually. Docker 20.10 added the host-gateway keyword specifically to remove the need for hardcoding IP addresses on Linux.
host.docker.internal Configuration in Docker Compose
The extra_hosts directive adds an entry to the container’s /etc/hosts file, mapping host.docker.internal to the bridge gateway. This is the standard way to configure host access in Docker Compose files, and it works across all three platforms when paired with host-gateway.
version: '3.9'
services:
app:
image: myapp:latest
ports:
- "8080:8080"
extra_hosts:
- "host.docker.internal:host-gateway"
Docker reads the host-gateway value, resolves the bridge network’s gateway address, and writes a corresponding line into the container’s hosts file. No IP address needs to appear in the Compose file — which matters when the gateway address varies between machines or environments. For teams setting up Node.js or npm on Linux, this pattern keeps the configuration portable from the start.
Using –add-host for host.docker.internal in docker run
The same mapping applies when launching containers through the CLI instead of Compose. The --add-host flag accepts host-gateway as a value since Docker 20.10.
docker run \ --rm \ -p 5433:5432 \ -e POSTGRES_PASSWORD=secret \ --add-host=host.docker.internal:host-gateway \ postgres:14
The flag creates a temporary /etc/hosts entry for the duration of the container session. Once the container stops, the mapping disappears. This is useful for one-off debugging runs without touching a Compose file.
When You Need host.docker.internal Configuration
The main use case is reaching a database, message broker, or API server that runs directly on the host machine rather than inside Docker. If your application container needs to connect to PostgreSQL or MySQL on localhost:5432, it can’t use localhost — inside the container, that address points back to the container itself.
host.docker.internal solves this by routing traffic out of the container to the host. Projects that run only containerized services don’t need it — standard container names handle inter-service communication. For developers managing port forwarding between Linux containers and the host OS, the hostname also removes the need to track dynamic IP assignments.
Testing Your host.docker.internal Setup
Start a simple HTTP server on the host to verify the connection works:
python3 -m http.server 8000
Then run a quick container test:
docker run --rm \ --add-host=host.docker.internal:host-gateway \ curlimages/curl \ curl -s http://host.docker.internal:8000
A successful response returns the directory listing from the host. If nothing comes back, check that the service is listening on 0.0.0.0 rather than 127.0.0.1 — containers cannot reach a service bound only to the loopback interface. You can confirm the hostname resolved correctly inside any running container with:
docker exec <container_name> getent hosts host.docker.internal
This should return 172.17.0.1 or whichever gateway IP Docker assigned. Similarly, when restarting NGINX on Linux during development, checking that the service binds to 0.0.0.0 is just as important before testing container access.
Troubleshooting host.docker.internal Errors
This means DNS resolution failed. The extra_hosts mapping is missing from the container. Add it to your Compose file or docker run command.
| Error | Cause | Fix |
|---|---|---|
| invalid IP address: host-gateway | Docker version below 20.10 | Update Docker Engine to 20.10 or later |
| Connection refused | Service bound to 127.0.0.1 only | Set bind-address to 0.0.0.0 in the service config |
| Connection timeout | Firewall blocking bridge traffic | Allow traffic from the 172.17.0.0/16 range |
| DNS not found | Missing extra_hosts entry | Add host.docker.internal:host-gateway to Compose |
If the gateway resolves but the connection still drops, verify the service isn’t filtering traffic by source IP. Some database configurations only accept connections from localhost by default — that rule blocks container-originated traffic regardless of what host.docker.internal resolves to. For developers checking running Linux processes, confirming the service is active before testing container access saves debugging time.
Alternatives to host.docker.internal
network_mode: host removes network isolation entirely. The container shares the host’s network stack, so localhost inside the container points directly to the host. No hostname mapping is needed, but all container ports become visible on the host immediately — which undermines network isolation.
services:
app:
image: myapp
network_mode: host
For Linux only, you can also hardcode the Docker bridge gateway:
extra_hosts: - "host.docker.internal:172.17.0.1"
This breaks if the gateway address changes, so host-gateway is the better approach for any setup shared across machines or running in multiple Linux container environments.
Cross-Platform Consistency with host.docker.internal
The practical benefit of using host-gateway in Docker Compose is that the same file runs without modification on macOS, Windows, and Linux. On macOS and Windows, Docker Desktop ignores the extra_hosts entry and uses its own built-in resolution. On Linux, it applies the mapping.
Teams with mixed operating systems — a common setup for most development teams — can commit a single docker-compose.yml and have consistent behavior across all machines. CI environments running Ubuntu or Debian with Docker Engine 20.10 also pick up the correct mapping without separate configuration files.
FAQs
What is host.docker.internal used for?
It lets Docker containers connect to services running on the host machine. It resolves to the Docker bridge gateway IP, giving containers a consistent hostname to reach the host without hardcoding IP addresses.
Why doesn’t host.docker.internal work on Linux by default?
Linux runs Docker natively without a VM layer, so the hostname isn’t injected automatically. You must add extra_hosts: ["host.docker.internal:host-gateway"] in your Compose file. Requires Docker Engine 20.10 or later.
How do I fix “invalid IP address: host-gateway”?
Your Docker version is below 20.10. The host-gateway keyword was introduced in that release. Update Docker Engine with apt-get install docker-ce or equivalent for your Linux distribution.
Does host.docker.internal work in Docker Swarm?
No. Docker Swarm does not support host-gateway or host.docker.internal. Swarm deployments require overlay networks or explicit host IP addresses for cross-node communication.
What IP does host.docker.internal resolve to?
It resolves to the Docker bridge network gateway — 172.17.0.1 on default installations. Run docker exec <container> getent hosts host.docker.internal to confirm the address inside a running container.
