EXPOSE vs publishing

EXPOSE in a Dockerfile only documents the port the app listens on; it opens nothing. Publishing with -p or ports: is what makes a port reachable from outside the server.

How it works

On a shared network, every port a container listens on is reachable by the other containers, exposed or not. In Compose, expose: is documentation for the other services and ports: publishes on the server. docker run -P publishes every EXPOSEd port on random server ports.

What breaks

Publishing the database or the API next to the reverse proxy opens doors nobody needs: only the entry point should have ports:. Everything else talks over the stack's network.

In compose.yaml

services:
  proxy:
    image: nginx:1.27
    ports:
      - "80:80"
  api:
    build: ./api
    expose:
      - "3000"

See it break, then fix it

Write the Dockerfile or the compose file, type the docker commands, and watch a simulated server run them through reboots, upgrades and traffic. Free to start.