Connecting to Docker Host from Docker Container

Published Updated

7 min read

Learn how to reach a service running on the Docker host, like a database, an API, a dev server or Ollama, from inside a container. The guide covers host.docker.internal on Docker Desktop, --add-host=host.docker.internal:host-gateway and Compose extra_hosts on Linux, and the three reasons the connection usually fails.

Inside a container, localhost is the container itself, not your machine. To reach a service on the host, use the special hostname host.docker.internal.

The short version:

shell

1# Docker Desktop (Mac, Windows, Linux): works out of the box
2docker run --rm curlimages/curl http://host.docker.internal:8000
3
4# Docker Engine on Linux: map the name to the host first
5docker run --rm --add-host=host.docker.internal:host-gateway curlimages/curl http://host.docker.internal:8000

In Docker Compose, the Linux equivalent is extra_hosts:

yaml

1services:
2 app:
3 image: curlimages/curl
4 command: ["http://host.docker.internal:8000"]
5 extra_hosts:
6 - "host.docker.internal:host-gateway"

If the name resolves but the connection is refused or times out, the problem is on the host side: the service listens only on 127.0.0.1, or a firewall drops the traffic. Both are covered below. All commands were tested on Docker Desktop for Mac and on Docker Engine 29 on Linux.

How host.docker.internal works

host.docker.internal is a hostname that points to your host machine from inside a container. Use it anywhere you'd normally write localhost: in a database URL, an API endpoint, or an app config.

On Docker Desktop (Mac, Windows and Linux), Docker resolves the name for you. No flags needed. In our test on Docker Desktop for Mac, even a service that listened only on 127.0.0.1 was reachable this way.

Docker Desktop also has gateway.docker.internal. It points to the gateway of Docker Desktop's internal VM, not to your machine, so don't use it to reach host services.

Docker Engine on Linux has no VM in between and doesn't create host.docker.internal by default. Without extra setup, the name simply doesn't resolve.

Docker Engine on Linux: add host-gateway

Docker Engine 20.10 and later supports a special value, host-gateway. It resolves to the host's address on the default bridge network (usually the docker0 interface). You map host.docker.internal to it, and Docker writes the line into the container's /etc/hosts.

With docker run

shell

1docker run -it --rm --add-host=host.docker.internal:host-gateway ubuntu:24.04 bash

Inside the container, check the mapping:

shell

1grep host.docker.internal /etc/hosts

You'll see something like:

shell

1172.17.0.1 host.docker.internal

The address depends on how Docker set up the bridge on your machine, and that's the point: you don't need to know it. The flag also works for containers on user-defined networks.

With Docker Compose

Add extra_hosts to every service that needs the host:

yaml

1services:
2 app:
3 image: my-app:latest
4 environment:
5 DATABASE_URL: postgres://app:secret@host.docker.internal:5432/app
6 extra_hosts:
7 - "host.docker.internal:host-gateway"

Then start it as usual with docker compose up. The same file works on Docker Desktop too, so you can commit it and share it with a team on mixed operating systems.

Change the gateway address

If host-gateway should point somewhere other than the default bridge, set host-gateway-ip in /etc/docker/daemon.json (or pass --host-gateway-ip to dockerd) and restart Docker. You rarely need this.

Test the connection

A quick way to check the whole chain is a tiny HTTP server on the host and curl from a container.

Step 1: Start a test server on the host

shell

1python3 -m http.server 8000

By default, the server listens on all interfaces, which is what you want for this test.

Step 2: Start a container

On Docker Desktop:

shell

1docker run -it --rm ubuntu:24.04 bash

On Docker Engine on Linux:

shell

1docker run -it --rm --add-host=host.docker.internal:host-gateway ubuntu:24.04 bash

Step 3: Install curl and call the host

shell

1apt-get update && apt-get install -y curl
2curl -I http://host.docker.internal:8000

You should get HTTP/1.0 200 OK and a log line in the terminal where the Python server runs. Type exit to leave the container. It's removed automatically because of --rm.

Why the connection fails on Linux

Each failure has its own error, so read it first.

Could not resolve host

shell

1curl: (6) Could not resolve host: host.docker.internal

The container doesn't know the name. Add --add-host=host.docker.internal:host-gateway or extra_hosts as shown above. Python apps report the same problem as Name or service not known. That's the classic error when Open WebUI or another app in Docker tries to reach Ollama on port 11434 of the host on Linux.

Connection refused

shell

1curl: (7) Failed to connect to host.docker.internal port 8000 after 0 ms: Could not connect to server

The name resolves, but nothing accepts the connection on that address. Most often the service on the host listens only on 127.0.0.1. Traffic from a container arrives on the bridge interface, not on loopback, so the service never sees it.

Make the service listen on all interfaces (0.0.0.0) or on the bridge address. Some common examples:

  • Ollama binds 127.0.0.1:11434 by default. Set OLLAMA_HOST=0.0.0.0:11434 for the service:

shell

1sudo systemctl edit ollama.service
2# add under [Service]:
3# Environment="OLLAMA_HOST=0.0.0.0:11434"
4sudo systemctl daemon-reload
5sudo systemctl restart ollama
  • PostgreSQL: set listen_addresses in postgresql.conf and allow the Docker subnet in pg_hba.conf.
  • MySQL and MariaDB: set bind-address in the server config.
  • Dev servers (Vite, Next.js, Django and others): start them with --host 0.0.0.0 or the framework's equivalent.

Listening on 0.0.0.0 also exposes the service to your network. On a laptop or a server with a public IP, protect it with a firewall rule that allows only the Docker subnets.

Connection timed out

shell

1curl: (28) Connection timed out after 5001 milliseconds

A firewall on the host drops the packets. With ufw and its default deny incoming policy, this happens out of the box. Allow the port from Docker networks:

shell

1sudo ufw allow from 172.16.0.0/12 to any port 8000 proto tcp

A rule like ufw allow in on docker0 covers only the default bridge. Docker Compose creates its own network with a separate br-... interface, so that rule doesn't match Compose containers. A subnet rule covers both. 172.16.0.0/12 matches Docker's default address pools; if you changed them in daemon.json, use your ranges.

On a cloud server, the provider's firewall or security group doesn't matter here: the traffic never leaves the machine.

Other ways to reach the host

Host network mode

shell

1docker run --rm --network host curlimages/curl http://127.0.0.1:8000

With --network host, the container shares the host's network stack. localhost inside the container is the host, so even services bound to 127.0.0.1 are reachable. In Compose, it's network_mode: host.

The trade-offs:

  • The container loses network isolation and sees every port on the host.
  • Port publishing (-p) is ignored. Docker prints WARNING: Published ports are discarded when using host network mode.
  • Two containers can't listen on the same port.

On Docker Engine for Linux, it works out of the box. Docker Desktop supports host networking since version 4.34 as an opt-in setting (Settings > Resources > Network > Enable host networking). It works for Linux containers and TCP/UDP only. For most setups, host.docker.internal is the simpler choice.

The bridge IP (172.17.0.1)

Many older answers hardcode 172.17.0.1, the usual address of docker0. It works until it doesn't. Docker picks a free subnet at startup, admins change it with bip, and Docker Desktop doesn't use it at all. In our Linux test, docker0 came up as 172.18.0.1 because 172.17.0.0/16 was already taken, and requests to 172.17.0.1 were refused.

host-gateway resolves the right address for you. Use it instead of a hardcoded IP.

Podman

Podman adds both host.containers.internal and host.docker.internal to /etc/hosts automatically, so most Docker examples work without flags. If it can't detect the host address, it skips the entries silently. In that case, set host_containers_internal_ip in containers.conf.

Controlling other containers: docker.sock, not host.docker.internal

host.docker.internal gives a container network access to services on the host. It doesn't let a container start, stop or inspect other containers.

For that, the container needs the Docker API. The usual way is to mount the Docker socket:

shell

1docker run --rm -v /var/run/docker.sock:/var/run/docker.sock docker:cli docker ps

This is how backup tools stop containers before a snapshot, and how web UIs like Portainer manage them.

Be careful with it. Access to the Docker socket is root access to the host. Anyone who controls that container can start a new container that mounts / from the host and read or change any file. Mount the socket only into images you trust. If a tool needs just a few API calls, put a filtering proxy like docker-socket-proxy in front of the socket, or run Docker in rootless mode.

Using host.docker.internal in production

host.docker.internal is great for local development: a database on your laptop, Ollama on a GPU box, a dev server in your IDE.

In production, it usually means a dependency lives outside the container setup and is reached through a side door. Run the dependency as a container on the same Docker network and address it by service name, or give it a proper DNS name. That way you don't depend on bridge addresses and host firewall rules.

Wrapping up

From a container, localhost is the container. To reach the host, use host.docker.internal. Docker Desktop resolves it for you. On Docker Engine for Linux, add --add-host=host.docker.internal:host-gateway or extra_hosts in Compose. If it still fails, check the error: no name means a missing mapping, refused means the service listens on 127.0.0.1, and a timeout means a firewall.

New to Docker? Start with how to run a Docker container on your local machine. Need the reverse direction, from the host into a container? See how to expose a port in a Docker container.

When containers outgrow a single machine and need scaling, rolling updates and monitoring, the next step is usually Kubernetes. If you'd like help with that, take a look at our Kubernetes consulting services.

Alex Kondratiev

Alex Kondratiev

Founder and CEO of ITsyndicate. DevOps engineer with 15+ years in cloud infrastructure, Infrastructure as Code, Kubernetes and automation, focused on secure, scalable and resilient systems.

More about Alex

Stay online.
Stay in command.