The trap
localhost inside a container refers to that container’s own loopback, not the host and not any other container. Calling http://localhost:8086 from inside my-app to reach openfga will be refused.
How it actually works
Containers on the same Docker network get Docker’s internal DNS. Service names resolve to container IPs automatically.
services:
openfga:
ports:
- "8086:8080" # host:container
my-app:
# reaches openfga at http://openfga:8080, not localhost:8086Port mappings (8086:8080) only apply to traffic from outside Docker — the host, a browser, curl on the host. Container-to-container traffic bypasses them entirely.
Rule of thumb
| Caller | URL |
|---|---|
| Host machine | http://localhost:<host-port> |
| Another container | http://<service-name>:<container-port> |