Dash0 Raises $110M Series B at $1B Valuation

  • 10 min read

How to Get the IP Address of a Docker Container

Container IPs feel like they should be the answer to a lot of Docker networking questions. They usually aren't. The fastest way to read one is docker inspect with a Go template, but the more important question is whether you should be looking at IPs in the first place.

Docker assigns IPs dynamically from each network's address pool. They exist for the lifetime of the container and vanish on stop. That impermanence is why production setups rarely reference containers by IP and use Docker's embedded DNS instead. This article shows the commands to grab the IP when you genuinely need it, and the patterns that mean you usually won't.

Get the IP with docker inspect

The standard command is docker inspect with a Go template that walks the NetworkSettings.Networks map:

bash
1
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' nginx
text
1
172.17.0.2

The range loop is there because a container can be attached to more than one network. If you know which network you want, you can address it directly by name:

bash
1
docker inspect -f '{{.NetworkSettings.Networks.bridge.IPAddress}}' nginx
text
1
172.17.0.2

For a container on a user-defined network, you'll often need the index function instead of dotted access — Go templates treat hyphens as a subtract operator, so a name like app-net can't appear as a field selector:

bash
1
docker inspect -f '{{(index .NetworkSettings.Networks "app-net").IPAddress}}' web

index handles arbitrary map keys, so it's the safe form for any network name that has hyphens, dots, or leading digits.

To dump the IP of every running container at once, combine docker ps with the same trick:

bash
1
docker inspect -f '{{.Name}} {{range .NetworkSettings.Networks}}{{.IPAddress}} {{end}}' $(docker ps -q)
text
123
/web 172.18.0.3
/db 172.18.0.2
/redis 172.17.0.4

Inconsistent subnets in that output are a useful debugging hint. Here web and db share 172.18.0.0/16 (a user-defined bridge), while redis is on 172.17.0.0/16, the default bridge. If you're wondering why two of your containers can't talk to each other, this is often the answer: they're not on the same network in the first place.

Inspect the whole network

docker network inspect shows the full configuration of a network, including every connected container with its IP, MAC, and aliases:

bash
1
docker network inspect bridge

The part you actually care about is the Containers block:

json
123456789101112
"Containers": {
"a4f8c9e12345...": {
"Name": "nginx",
"IPv4Address": "172.17.0.2/16",
"IPv6Address": ""
},
"b9c1d2f87654...": {
"Name": "redis",
"IPv4Address": "172.17.0.3/16",
"IPv6Address": ""
}
}

This is the better starting point when you don't already know the container name, or when you want to understand the layout of a network rather than the address of one specific container. It also shows the network's gateway address, which is what containers see as the "host" side of the bridge.

You probably want DNS, not an IP

Most questions phrased as "I need the container IP" are actually "I need container A to talk to container B." Hardcoding an IP works once and then breaks the next time you restart, because Docker can hand out a different address from the pool.

Docker runs an embedded DNS server at 127.0.0.11 inside every container, so containers can reach each other by name. There's one important catch: this only works on user-defined networks. The default bridge has no DNS resolution between containers, which is the single most common source of "why can't my containers see each other" confusion.

Create a network, put your containers on it, and the names just work:

bash
123
docker network create app-net
docker run -d --name db --network app-net postgres:17
docker run -d --name web --network app-net nginx
bash
1
docker exec web getent hosts db
text
1
172.18.0.2 db

getent hosts is a libc lookup, so it works on the stock nginx image without installing anything and tests DNS resolution directly rather than mixing it with ICMP reachability the way ping would.

In application code, your config points at postgres://db:5432/mydb and you forget IPs exist. The same applies in Docker Compose: every service in a compose.yaml is reachable by its service name from any other service in the same project, because Compose creates a user-defined network behind the scenes.

yaml
1234567
services:
web:
image: nginx
db:
image: postgres:17
environment:
POSTGRES_PASSWORD: example

In this file, web connects to the database at db:5432. No IPs, no manual network creation, no --link.

Accessing the host from a container

The other half of the IP question goes the opposite direction: a container that needs to reach something running on the host machine, like a database installed natively on your laptop or a service on a non-Docker port. The clean answer is the host.docker.internal hostname.

On Docker Desktop (macOS and Windows), host.docker.internal resolves out of the box and points to the host's gateway:

bash
1
docker exec web getent hosts host.docker.internal
text
1
192.168.65.2 host.docker.internal

On Linux it does not. You have to add it explicitly with --add-host and the special host-gateway value:

bash
123
docker run -d --name web \
--add-host=host.docker.internal:host-gateway \
nginx

In Compose:

yaml
12345
services:
web:
image: nginx
extra_hosts:
- "host.docker.internal:host-gateway"

The host-gateway value was introduced in Docker Engine 20.10 (December 2020), so older Linux installations don't have it. A workaround you'll still see in older guides is hardcoding 172.17.0.1, the default bridge gateway IP, but that breaks the moment you switch to a user-defined network with a different subnet. Use host.docker.internal when you can.

One thing worth knowing the first time you hit it: if the service on the host is bound only to 127.0.0.1, a container reaching out via host.docker.internal will get connection refused. The host process needs to listen on 0.0.0.0, or on the docker bridge gateway interface specifically, for container traffic to reach it.

Common pitfalls

A stopped container has no IP. If docker inspect returns an empty IP field, the container isn't running. The IP only exists while the network interface is attached, so restart it before reading.

The default bridge has no DNS. If you forgot --network and your container ended up on the default bridge, container names won't resolve. The legacy fix was --link, which is deprecated. Create a user-defined network with docker network create and reconnect, or just use Compose.

Compose service names are not container names. Compose prefixes container names with the project and adds an index suffix (myproject-db-1), but inside the network you address services by their short name (db). Don't confuse docker ps output with what's resolvable inside the network.

A container can have several IPs at once. If a container is attached to multiple networks, the range template prints all of them concatenated, which produces output that looks like one giant IP. Use the explicit index form when you need a specific one.

Final thoughts

By the time your container topology grows past a handful of services, watching IPs by hand stops scaling. The same applies to tailing container logs one by one and reading container resource usage with docker stats. You want to see which containers are running, which networks they're attached to, what they're talking to, and which ones are quietly failing health checks, without docker exec-ing into anything.

Dash0's infrastructure monitoring is OpenTelemetry-native and tracks every container's resource usage, status, and network activity alongside real-time logs and distributed traces, so you can see the whole topology in one place without writing inspect commands.

Start a free trial today to monitor your containers, networks, and logs in one view. No credit card required.