Back to blog

Docker EXPOSE vs PUBLISH: Understanding Container Port Mapping

When configuring containerized services with Docker, directing network traffic into your containers is a primary task. During setup, you will encounter two concepts for managing ports: the EXPOSE instruction inside a Dockerfile and the -p (or --publish) flag in the docker run command.

These parameters are frequently confused. Setting EXPOSE inside your build configuration does not make your web server accessible from your host machine browser.

In this guide, we will analyze the functional differences between EXPOSE and PUBLISH, examine container network bridges, and map ports.

1. The EXPOSE Instruction (Declarative Documentation)

The EXPOSE command is written inside a Dockerfile. It acts as a metadata declaration between the person who builds the image and the person who runs the container.

# Inside Dockerfile
FROM node:18
WORKDIR /app
COPY . .
RUN npm install

# Declare the port the application listens on
EXPOSE 3000

CMD ["node", "server.js"]

What EXPOSE Does

  • Documentation: It declares which ports the application inside the container is configured to listen on.
  • Internal Network Awareness: It serves as a hint to Docker. When containers are attached to the same user-defined network, they can communicate with each other using these ports automatically, without exposing them to the host computer.
  • No Host Port Access: Setting EXPOSE 3000 does not open port 3000 on your host machine. If you attempt to access http://localhost:3000 from your host web browser, the connection will be refused.

2. The -p or --publish Flag (Actual Port Forwarding)

To make a containerized application accessible from your host machine (or the outside internet), you must publish the port when launching the container using the CLI.

This is configured using the lower-case -p flag:

# Publish port 3000 inside the container to port 8080 on the host machine
docker run -p 8080:3000 my-node-app

The syntax structure is: docker run -p [host_port]:[container_port] [image_name]

What PUBLISH Does

  • Active Port Binding: It tells the Docker Daemon to allocate and listen on port 8080 of the host operating system.
  • Network Address Translation (NAT): Docker routes all incoming packets sent to host port 8080 and forwards them to port 3000 inside the target container.
  • Host Access: You can open http://localhost:8080 in your host browser to contact the application successfully.

The -P Flag (Publish All Exposed Ports)

Docker includes an upper-case -P flag. This command reads the EXPOSE metadata declared in the image's Dockerfile and binds all listed ports to random high-number ports (typically in the 32768–60999 range) on the host:

# Run container, mapping all exposed ports to random host ports
docker run -P my-node-app

You can run docker ps to view which random host port Docker allocated:

PORTS
0.0.0.0:32768->3000/tcp

In this case, traffic arriving at host port 32768 is routed to container port 3000. This is useful for scaling multiple instances of the same image on a single host without manually managing port collisions.

Key Differences Summary

Feature EXPOSE PUBLISH (-p / -P)
Location Defined inside Dockerfile Defined during docker run command
Primary Purpose Documentation and internal port mapping Opens port on host and forwards traffic
Host Accessibility Inaccessible to host Accessible to host (and public networks)
Port Mapping Single port number (e.g., 3000) Mapped pair (e.g., 8080:3000 or random)

Conclusion

The EXPOSE instruction is a build-time metadata declaration that informs developers which ports are monitored inside the container, facilitating inter-container communication on shared networks. The -p flag is a runtime command that binds container ports to host interfaces, allowing external traffic to navigate through the host system to reach your containerized services.