Skip to content

Publishing and Exposing Ports

There is a common source of confusion in Docker networking: the difference between EXPOSE in a Dockerfile and -p on docker run.

  • EXPOSE is documentation. It tells humans and tools which port the application listens on inside the container. It does not open any port or make anything reachable from outside.
  • -p (or --publish) actually binds a host port to a container port. This is what makes traffic from outside the container reach your application.

If you only add EXPOSE 80 to your Dockerfile and forget -p, the application inside the container is listening, but nothing from the host or the internet can reach it.

The -p flag takes the form host_port:container_port:

Terminal window
docker run -d --name webserver -p 8080:80 nginx:alpine

This binds port 8080 on the host to port 80 inside the container. A request to http://localhost:8080 on the host is forwarded to the container’s port 80.

List port mappings for a running container

Section titled “List port mappings for a running container”

Use docker port to see all port bindings for a named container:

Terminal window
docker port webserver

Output example:

80/tcp -> 0.0.0.0:8080

Once published, the port is reachable from the host:

Terminal window
curl http://localhost:8080

By default, Docker binds to all host interfaces (0.0.0.0). To restrict binding to the loopback interface only (no external access):

Terminal window
docker run -d --name local-only -p 127.0.0.1:9090:80 nginx:alpine

This is useful for development environments where you want the service reachable only from the same machine.

Pass -p multiple times to publish more than one port:

Terminal window
docker run -d --name multi -p 8080:80 -p 8443:443 nginx:alpine

Let Docker pick the host port (ephemeral ports)

Section titled “Let Docker pick the host port (ephemeral ports)”

Omit the host port to let Docker assign an available port automatically:

Terminal window
docker run -p 80 nginx:alpine

Docker picks a free port in the ephemeral range (typically 32768–60999). Use docker port to find out which port was assigned.

# Publish port 80 of nginx to host port 8080
docker run -d --name webserver -p 8080:80 nginx:alpine

# List port mappings
docker port webserver

# Curl the published port
curl http://localhost:8080

# Bind to loopback only
docker run -d --name local-only -p 127.0.0.1:9090:80 nginx:alpine

# Multiple ports
docker run -d --name multi -p 8080:80 -p 8443:443 nginx:alpine

# Cleanup
docker stop webserver local-only multi && docker rm webserver local-only multi
OptionBenefitCost
Publish to 0.0.0.0 (all interfaces)Reachable from every machine on the network — good for a service that genuinely needs to be publicOpens access wider than necessary if the service should only be used locally
Publish to 127.0.0.1 onlyRestricts access to the same host — saferNot reachable from any other machine on the network; needs a flag change to open it up later
  • Assuming EXPOSE in a Dockerfile actually publishes the port, when it is only documentation of which port the container listens on — only -p/--publish actually maps it so it can be reached.
  • Accidentally publishing to 0.0.0.0 for a service that should only be reachable from localhost, like a database or an admin panel, exposing it to the network unnecessarily.
  • Publishing the same port from multiple containers on one host, causing docker run to fail because another container already holds that port.

💡 Real-world example

A common deployment pattern publishes ports only from a single reverse proxy (such as Traefik or nginx) at 0.0.0.0:80 and 0.0.0.0:443, while every other backend service publishes no ports at all and talks over the internal network — leaving exactly one point exposed to the outside.

What does the EXPOSE instruction in a Dockerfile actually do?
You run: docker run -p 9000:3000 myapp. Which of the following correctly describes the port mapping?
How do you restrict a published port so it is only reachable from the local machine (loopback interface)?
Which command lists the port mappings of a running container named 'webserver'?