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:
1docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' nginx
1172.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:
1docker inspect -f '{{.NetworkSettings.Networks.bridge.IPAddress}}' nginx
1172.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:
1docker 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:
1docker inspect -f '{{.Name}} {{range .NetworkSettings.Networks}}{{.IPAddress}} {{end}}' $(docker ps -q)
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:
1docker network inspect bridge
The part you actually care about is the Containers block:
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:
123docker network create app-netdocker run -d --name db --network app-net postgres:17docker run -d --name web --network app-net nginx
1docker exec web getent hosts db
1172.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.
1234567services:web:image: nginxdb:image: postgres:17environment: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:
1docker exec web getent hosts host.docker.internal
1192.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:
123docker run -d --name web \--add-host=host.docker.internal:host-gateway \nginx
In Compose:
12345services:web:image: nginxextra_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.