When running Docker Desktop on Windows, you should generally prefer the DNS name
host.docker.internal over the IP address
172.17.0.1 to connect to services running on your host machine. The hostname is dynamically resolved by Docker’s internal DNS server and remains stable across network interface changes, whereas the IP address is tied to the specific bridge network configuration which can change if Docker restarts or if multiple networks are involved. Using the hardcoded IP risks breaking connectivity unexpectedly during routine maintenance or updates.
Understanding the Default Bridge Network
By default, Docker creates a bridge network named
bridge with the subnet
172.17.0.0/16. The gateway for this network is typically assigned the first available IP in that range, which is
172.17.0.1. This address represents the host’s interface within the Docker virtual environment. For years, developers relied on this static IP to route traffic from inside a container back to applications running on their physical Windows machine, such as local databases, API servers, or debugging endpoints.
However, relying on
172.17.0.1 has significant drawbacks. If you create additional custom bridge networks, each will have its own gateway IP (e.g.,
172.18.0.1,
172.19.0.1). A container attached to a custom network cannot reach the host via
172.17.0.1 because that address belongs to a different network segment. Furthermore, while
172.17.0.1 is standard for the default bridge, it is not guaranteed to remain constant across all Docker versions or installation types. If you reinstall Docker or clear your data directory, the underlying network interfaces may be recreated with different IPs, causing silent failures in your application connections.
The Role of host.docker.internal
To solve these portability issues, Docker Desktop for Windows introduced the special DNS name
host.docker.internal. This is not a real DNS record in your system’s resolver but rather a feature implemented by the Docker Desktop daemon. When a container queries this hostname, Docker intercepts the request and resolves it to the correct IP address of the host machine relative to the container’s current network.
This abstraction layer ensures that your containerized applications can always find the host, regardless of whether they are on the default bridge network or a user-defined overlay network. It decouples your application logic from the underlying network topology. You do not need to configure any extra settings to use it; it is available out-of-the-box for containers run via Docker Desktop.
Comparing Reliability and Portability
The primary difference between the two methods lies in their resilience to environmental changes.
172.17.0.1 is a concrete network identifier. If your Windows machine gains a new Wi-Fi adapter, switches to a different subnet, or if Docker’s internal networking stack is rebuilt, this IP might shift or become unreachable depending on how the routing tables are updated. In contrast,
host.docker.internal is handled by the Docker engine itself. The engine knows exactly how to route packets back to the host interface, making it immune to external network reconfigurations.
Additionally, using the hostname improves code readability. Seeing
host.docker.internal in a connection string immediately signals to other developers that the service is located on the host machine, whereas
172.17.0.1 looks like an arbitrary internal IP that requires documentation to explain its purpose. This clarity reduces onboarding time and minimizes configuration errors in team environments.
If you are managing complex multi-container setups, you might also consider optimizing your images to reduce overhead. For more on efficient image management, see
Optimizing WSL Docker Image Sizes on Non-Admin PCs.
Implementation and Usage
Using
host.docker.internal is straightforward. You simply replace your localhost references with this hostname in your application configurations. For example, if your database connection string previously pointed to
localhost:5432, you would update it to
host.docker.internal:5432. This works seamlessly with most modern frameworks and libraries that support standard DNS resolution.
It is important to note that this feature is primarily supported by Docker Desktop on Windows and macOS. On Linux hosts, especially those using rootless Docker or older versions,
host.docker.internal may not be available by default. In such cases, you might need to manually add the hostname to the container’s
/etc/hosts file or use the host’s actual IP address obtained via network inspection commands. However, since we are focusing on Windows, Docker Desktop handles this resolution automatically without manual intervention.
For users experiencing network instability after system updates, checking your WSL configuration is crucial. Learn more about
Troubleshooting Windows Restart Issue: Enabling WSL Virtual Machine Platform for Docker to ensure your virtual infrastructure remains stable.
When to Use Each Approach
In almost all modern development scenarios on Windows,
host.docker.internal is the recommended choice. It offers better reliability, cleaner code, and compatibility with custom networks. The only rare exception might be in highly constrained environments where DNS resolution is disabled or blocked for security reasons, though this is uncommon in standard Docker Desktop installations. If you are migrating legacy systems that strictly require IP-based routing, you may still encounter
172.17.0.1, but you should plan to refactor these connections to use the hostname to prevent future breakages.
Frequently Asked Questions
Does host.docker.internal work on Linux Docker?
No,
host.docker.internal is not supported natively on standard Linux Docker installations. It is a feature specific to Docker Desktop on Windows and macOS. On Linux, you must use the host’s actual IP address or add the hostname manually to the container’s hosts file.
Can I use host.docker.internal with docker-compose?
Yes, you can use
host.docker.internal directly in your
docker-compose.yml files. Just reference it in the environment variables or command arguments for your services, and Docker will resolve it correctly at runtime without needing special network configuration.
Why did my connection fail after restarting Docker?
If you were using
172.17.0.1, the IP address might have changed if Docker recreated the default bridge network. Switching to
host.docker.internal prevents this issue because the DNS name is resolved dynamically by the Docker engine every time the container starts, ensuring it always points to the correct host interface.
Is there a performance penalty for using DNS names?
The performance impact is negligible for local development. The DNS resolution happens locally within the Docker engine and is cached. While it adds a tiny fraction of a millisecond compared to direct IP usage, the benefit of stability and ease of maintenance far outweighs any minor latency increase.
How do I check if host.docker.internal is working?
You can verify functionality by running a simple ping command inside a container. Execute
docker run --rm alpine ping -c 1 host.docker.internal. If the ping succeeds, the hostname is resolving correctly. If it fails, ensure you are using Docker Desktop and not a bare Linux installation.