16.4.2.2 Compose Wrong Internal Port
A focused guide to Compose Wrong Internal Port, connecting core concepts with practical Docker and container operations.
A Compose wrong internal port error occurs when a dependent service is configured to reach another service using a port number that does not actually match what that target service is genuinely listening on inside its own container, a mistake that is easy to make because the port number relevant for inter-service communication within Compose is the container's internal port, not whatever external, host-published port might also be configured and visible in the same file.
Internal port is what matters for service-to-service communication
When one service connects to another by service name within the same Compose network, the port used in that connection string needs to match the target's actual internal listening port, entirely independent of whatever ports mapping that target service might also have configured for host access:
services:
api:
environment:
- DATABASE_URL=postgres://db:5433/app
db:
image: postgres
ports:
- "5433:5432"
The ports mapping here exposes the database on host port 5433, mapped to the database's actual internal port 5432; but api's connection string incorrectly uses 5433, the host-facing port, for its internal, container-to-container connection, which fails because nothing inside the Docker network is actually listening on port 5433 at all, that mapping only applies to connections arriving from the host's own external interface.
services:
api:
environment:
- DATABASE_URL=postgres://db:5432/app
The corrected connection string uses 5432, the database's genuine internal port, which is what another container on the same network needs to target, regardless of whatever external port mapping might separately exist for host-level access.
Why this specific mistake is easy to make
The confusion arises because a single Compose file often displays both the internal port (implicitly, through the application's own default behavior) and an external host port mapping (explicitly, in the ports section) close together, and it is intuitive, but incorrect, to assume the externally visible, explicitly written port number is the one relevant for every kind of connection, including container-to-container ones:
ports:
- "8080:3000"
Reading this mapping, it is easy to mistakenly conclude that "the port is 8080," when in fact 8080 is relevant only to connections originating from outside the Docker network entirely; any other container on the same Compose network needs to use 3000, the actual internal port the application is listening on, to reach this service directly.
Verifying the actual internal listening port directly
Rather than relying on assumption or a misread of the ports mapping, confirming the target service's actual internal listening port directly removes any ambiguity:
docker compose exec db netstat -tlnp
tcp 0.0.0.0:5432 LISTEN
This directly confirms what port the application inside the container is genuinely bound to, which is the only port number relevant for any other container attempting to reach it over the shared Compose network.
Services with no published ports at all
It is worth recognizing explicitly that a service does not need any ports mapping at all for other services on the same Compose network to reach it; ports mappings are exclusively about host-level, external access, and many services that only need to be reached by other containers within the same stack correctly have no ports entry whatsoever:
services:
db:
image: postgres
# no ports mapping; reachable by api on its internal port regardless
api:
environment:
- DATABASE_URL=postgres://db:5432/app
This is generally the more secure default for any service that genuinely never needs direct access from outside the Docker network, since omitting the ports mapping entirely removes that external exposure path while leaving internal, service-to-service connectivity fully intact and unaffected.
Internal port mismatches with custom application configuration
A target service running with a custom configuration that changes its default listening port internally produces a mismatch if the dependent service's connection configuration was written assuming the software's ordinary, unmodified default port:
services:
db:
image: postgres
command: ["postgres", "-p", "5433"]
api:
environment:
- DATABASE_URL=postgres://db:5432/app
Here, the database has been explicitly configured to listen internally on a non-default port (5433), but the dependent service's connection string still assumes the standard default (5432); checking for any such custom port configuration on the target service is worth doing explicitly whenever the standard, expected default port does not seem to be working as anticipated.
Multiple ports on the same service
A service exposing more than one internal port, an application server alongside a separate metrics or admin endpoint on a different port, requires the dependent service to target whichever specific internal port corresponds to the actual functionality it needs, not just any port the target happens to be listening on:
services:
api:
expose:
- "3000"
- "9090"
A monitoring service needing to scrape metrics should target port 9090 specifically, while a client needing the actual application functionality should target 3000; conflating the two, or assuming either port serves the same purpose, produces a connection that may succeed at the network level while still failing functionally, since the wrong endpoint was reached even though the port itself was technically open.
Common mistakes
- Using a service's external, host-published port number for an internal, container-to-container connection, when only the internal port is relevant for that kind of communication.
- Assuming a
portsmapping is required for one Compose service to reach another, when internal connectivity works regardless of whether any host-level port publishing exists at all. - Not verifying a target service's actual internal listening port directly when a connection fails, relying instead on an assumption about the software's standard default port.
- Overlooking a custom configuration that changes a service's actual internal listening port away from its software's ordinary default.
- Conflating multiple distinct ports exposed by the same service, connecting to the wrong one for the specific functionality actually needed.
A Compose wrong internal port error is resolved by remembering that container-to-container communication within a Compose stack always uses the target's actual internal listening port, entirely independent of any host-facing port mapping that might also exist, and verifying that internal port directly, rather than assuming it based on the more visible, externally-facing port number, when a connection between two services in the same stack is not working as expected.