How to Fix 'host.docker.internal' Not Resolving Inside Docker Containers on Linux
Threat/Impact Level: HIGH | Exploitability/Downtime Risk: HIGH | Time to Fix: 10 mins
TL;DR
- What broke:
host.docker.internalis a DNS alias Docker injects automatically on macOS and Windows but does not inject on Linux by default, causingName or service not knownerrors inside containers trying to reach the host machine. - How to fix it: Pass
--add-host=host.docker.internal:host-gatewaytodocker run, or add the equivalentextra_hostsblock in yourdocker-compose.yml. - Use the Client-Side Sandbox above to paste your
docker runcommand or Compose file and auto-generate the corrected config.
The Incident (What Does the Error Mean?)
Raw error output from inside the container:
curl: (6) Could not resolve host: host.docker.internal
or
getaddrinfo ENOTFOUND host.docker.internal
or a silent TCP timeout when your app tries to connect to host.docker.internal:5432.
Immediate consequence: Any containerized service that dials back to a process on the host — a locally running Postgres, a license server, a mock API, a sidecar agent — is completely severed. On Linux CI runners (GitHub Actions, GitLab, Jenkins agents), this silently breaks integration test suites that assume Docker Desktop behavior.
The Attack Vector / Blast Radius
This is a platform-behavior mismatch that becomes a security misconfiguration when engineers work around it incorrectly.
The dangerous workarounds engineers reach for under pressure:
--network=host— Collapses all container network isolation. Every port the container opens is now directly exposed on the host's network interfaces. In a multi-tenant CI environment or a cloud VM, this is a lateral movement vector.- Hardcoding the host's LAN IP (
192.168.1.x) — Breaks portability, fails in CI, and leaks internal network topology into version-controlled config files. - Disabling the firewall rule blocking container-to-host traffic — Leaves the host's internal services exposed to all containers on the bridge network.
Blast radius if --network=host is used in production: A compromised container has direct socket access to every service bound to 127.0.0.1 on the host, including metadata endpoints, local Kubernetes API servers, and any unprotected admin interfaces.
How to Fix It (The Solution)
Basic Fix — docker run
The host-gateway special value was introduced in Docker Engine 20.10. It resolves to the host's gateway IP on the container's default bridge network — exactly what host.docker.internal should point to.
- docker run --rm -it myapp:latest
+ docker run --rm -it --add-host=host.docker.internal:host-gateway myapp:latest
Basic Fix — docker-compose.yml
services:
app:
image: myapp:latest
+ extra_hosts:
+ - "host.docker.internal:host-gateway"
Enterprise Best Practice — Enforce It at the Compose Level for All Services
If multiple services need host access, define it once using a YAML anchor and merge it, preventing any service from being missed during a refactor:
- services:
- app:
- image: myapp:latest
- worker:
- image: myworker:latest
+ x-host-access: &host-access
+ extra_hosts:
+ - "host.docker.internal:host-gateway"
+
+ services:
+ app:
+ image: myapp:latest
+ <<: *host-access
+ worker:
+ image: myworker:latest
+ <<: *host-access
Verify the Fix
From inside the running container:
# Confirm DNS resolves
getent hosts host.docker.internal
# Confirm TCP connectivity to a host service (e.g., Postgres on 5432)
nc -zv host.docker.internal 5432
Expected output:
172.17.0.1 host.docker.internal
Connection to host.docker.internal 5432 port [tcp/postgresql] succeeded!
💡 Tired of pasting proprietary configs into ChatGPT? Generic AI tools log your company's ARNs, DB strings, and private keys. StackEngine is a zero-backend, pure Client-Side WASM utility. Drop your failing config into the sandbox above. We redact your secrets locally in the browser and auto-generate the refactored code using your own API key.
Prevention in CI/CD
1. Checkov Policy — Block --network=host Workarounds
Add a custom Checkov check or use the built-in CKV_DOCKER_2 to flag network_mode: host in any Compose file committed to the repo:
checkov -f docker-compose.yml --check CKV_DOCKER_2
2. OPA/Conftest Policy for Compose Files
# policy/docker_compose.rego
package docker.compose
deny[msg] {
service := input.services[name]
service.network_mode == "host"
msg := sprintf("Service '%v' uses network_mode: host. Use extra_hosts: host.docker.internal:host-gateway instead.", [name])
}
Run in CI:
conftest test docker-compose.yml --policy policy/
3. GitHub Actions — Enforce host-gateway on Linux Runners
Linux-hosted GitHub Actions runners are the most common environment where this breaks. Add a validation step before integration tests:
- name: Validate docker-compose host routing
run: |
grep -q 'host-gateway' docker-compose.yml || \
(echo "ERROR: Missing host.docker.internal:host-gateway in docker-compose.yml" && exit 1)
4. Docker Engine Version Gate
host-gateway requires Docker Engine ≥ 20.10. Pin your CI runner's Docker version and add a pre-flight check:
DOCKER_VERSION=$(docker version --format '{{.Server.Version}}')
required="20.10.0"
if [ "$(printf '%s\n' "$required" "$DOCKER_VERSION" | sort -V | head -n1)" != "$required" ]; then
echo "Docker Engine < 20.10 detected. host-gateway is unsupported. Upgrade required."
exit 1
fi