Docker Compose ports versus expose
Publish a port deliberately and understand why expose neither starts a listener nor enforces network isolation.
ports publishes a container port through the Docker host. expose declares intended container ports without publishing them. It is documentation and metadata, not a firewall rule: services on the same Docker network can normally reach listening ports whether or not expose lists them.
Publish a host port
For an HTTP service:
services:
web:
image: nginx:stable
ports:
- "127.0.0.1:8080:80"
The host's loopback port 8080 forwards to port 80 in the container. Omitting the IP, as in "8080:80", normally publishes on all host interfaces. Actual external reachability still depends on routing, firewall rules, and platform behavior; publication is not proof that every machine can connect.
A container-only number requests an automatically allocated host port:
services:
web:
image: nginx:stable
ports:
- "80"
Find the allocation with docker compose port web 80 or docker compose ps. It may change when the container is recreated. Quote short-syntax port values in YAML.
Communicate within the project network
services:
database:
image: mysql:8.4
environment:
MYSQL_ROOT_PASSWORD: "${MYSQL_ROOT_PASSWORD:?Set a database password}"
expose:
- "3306"
app:
image: myapp:1.2
environment:
DB_HOST: database
DB_PORT: "3306"
This sketch supplies the database's required initialization setting; use your deployment's secret-management approach for real credentials. Both services join the default project network and the app connects to database:3306. The application must handle database startup/readiness and use valid credentials.
Removing expose does not close port 3306 to peers on that network. Conversely, adding it does not make MySQL start, listen on the right address, or authorize clients. Services attached to separate networks do not become connected merely because they appear in the same Compose file.
FAQ
Can the host reach a port without ports?
It is not published as a host-port mapping. Native Linux hosts may nevertheless reach bridge-container addresses directly, subject to routing and filtering. Docker Desktop has different VM networking. Therefore, “not published” is more precise than “impossible for the host to access.”
Should databases publish ports?
Leave them unpublished when only application peers need access. For deliberate host-side administration, a loopback-bound mapping such as "127.0.0.1:3306:3306" narrows exposure. For remote administration, design authentication, transport protection, and network restrictions explicitly.
What creates a real boundary?
Network membership, routing and firewall policy, host configuration, and application authentication. expose describes intent; it does not enforce that boundary.
Contact us
Join the community
to join the community
Try Guance
Start online and pay only for what you use.
Get startedChoose a Guance plan