Initializing Enclave...

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.internal is a DNS alias Docker injects automatically on macOS and Windows but does not inject on Linux by default, causing Name or service not known errors inside containers trying to reach the host machine.
  • How to fix it: Pass --add-host=host.docker.internal:host-gateway to docker run, or add the equivalent extra_hosts block in your docker-compose.yml.
  • Use the Client-Side Sandbox above to paste your docker run command 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:

  1. --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.
  2. 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.
  3. 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

Related Diagnostics

"Part of the Security Utility Matrix."

View all 140 Security Tools →