Docker Compose Ports Explained: Host Ports, Container Ports, and Conflicts
Understand Docker Compose ports syntax, host-to-container mappings, localhost access, expose vs ports, and common port conflict errors in docker-compose.yml files.
Most Docker Compose port bugs are mapping bugs
Docker Compose port syntax looks small, but it controls how traffic reaches your service. The left side is the host port on your machine. The right side is the container port inside the service. Confusing those two is the quickest way to create a localhost bug or a port collision.
When to use this guide
Local development
Map a web app, API, database, queue, or admin panel to a predictable localhost port.
Compose reviews
Read a compose file and understand which services are exposed to the host.
Port conflicts
Find two services trying to bind the same host port before you run docker compose up.
Safer defaults
Decide when a service needs ports and when it should stay internal on the compose network.
How to read Compose ports
Start with host:container
"3000:80", open localhost:3000 on the host. Docker forwards that traffic to port 80 inside the container.Check protocol only when needed
"5353:5353/udp" maps UDP. Without a suffix, Docker uses TCP.Use expose for internal visibility
expose documents internal ports for other services. It does not publish the port to localhost.Inspect the full file visually
Docker Compose port examples
| Task | Input | Result |
|---|---|---|
| Basic web app | "3000:3000" | localhost:3000 reaches container port 3000. |
| Nginx container | "8080:80" | localhost:8080 reaches port 80 inside the container. |
| Bind to localhost only | "127.0.0.1:5432:5432" | The database is available only from the local machine. |
| Internal only | expose: ["6379"] | Other compose services can talk to Redis, but the host cannot. |
How to choose a Docker Compose port mapping
A port mapping is a small operational decision. It decides who can reach a service, whether two services collide, and whether local development matches production closely enough to catch real bugs.
Browser-facing app
"3000:3000", so teammates and test scripts can use the same URL every time.Database for local tools
127.0.0.1 when possible, for example "127.0.0.1:5432:5432", to avoid exposing the database on your network.Internal service
Temporary debugging
Port debugging checklist
- 1Read mappings as host port first, container port second.
- 2Check for duplicate host ports across all services in the compose file.
- 3Use
127.0.0.1bindings for local-only databases, queues, and admin panels. - 4Remove published ports from services that only need internal compose-network access.
ports vs expose
Use ports when a human, browser, test runner, or external client on the host needs to connect. Use expose when only other services in the compose network need to know about the port.
Port rule
Related workflow
This guide is designed to pair with the tool linked below. Use the article to understand the workflow, then open the tool with a real sample so you can validate the result instead of copying a generic answer from a search result.
Common mistakes to avoid
- Reading
"8080:80"backwards and openinglocalhost:80. - Publishing databases and internal queues to every network interface during local development.
- Using the same host port for two services.
- Assuming
exposemakes a service available from the browser.
FAQ
Which side of Docker Compose ports is localhost?
"8080:80", 8080 is the host port.Why does Docker say port is already allocated?
Do services need ports to talk to each other?
Try it in Docker Compose Viewer
Related Tools
Try It Now
Put this guide into practice with our free tools. No sign-up required.
Open Docker Compose Viewer