There is a big difference between exposing a Docker port and publishing it. Publication means that anyone can see the exposed port, regardless of who they are. Exposure means that someone has taken the time to document and publish the port, so others can potentially use it without having to worry about security.
Exposing a Port
Ports are exposed via EXPOSE instructions in an image’s Dockerfile:
Exposing a port doesn’t have any immediate effect though. The statement only communicates that the application inside the container listens on port 80. It does not bind the port to your host’s network interfaces.
As an image author, listing ports used by your workload with EXPOSE helps users configure appropriate port forwarding rules when they start a container. This is particularly important when non-standard ports are used: while a web server can be expected to listen on port 80, users won’t be able to guess the port used by a custom socket server.
Exposed ports are visible when you list your containers with docker ps. They’ll show up in the PORTS column, even though they won’t actually be accessible outside the container. This gives you a simple way of checking which ports the software inside a container is listening on.
You can inspect ports exposed by an image without starting a container by using docker inspect. Substitute your image’s tag or ID instead of demo-image:
Publishing Ports
Publishing a port makes it accessible from outside the container. It lets you take a port you’ve discovered by an EXPOSE instruction, then bind a host port to it.
Ports are exposed with the -p flag for the docker run command:
This command binds port 8080 on your Docker host to 80 inside your new container. Now you can visit http://localhost:8080 to access the container’s port. If you run docker ps, you’ll see the PORTS column now shows this mapping. The exposed container port 80 has been published to the host.
The -p flag can be used without specifying a port to bind to:
This variant will bind port 80 in the container to a random port on the host. You can check the port that’s been assigned by running docker ps.
-p also supports publishing a port to specific network interfaces:
Here the container port 80 will only be accessible via port 8080 on the host’s local loopback address. This protects your container from network calls made by your other devices.
Publishing All Exposed Ports
You can start a container with the –publish-all flag to have Docker automatically publish all the exposed ports listed in the image’s Dockerfile:
This will assign random free ports on your host to each exposed port in the container. Use the docker ps command to see the port assignations that have been made. This simplifies starting a new container from an image that requires several non-standard ports to be opened.
You can combine –publish-all with explicit -p mappings. In this case, a mapping created by a -p flag will override the random port assigned by –publish-all.
When You Don’t Need to Publish a Port
You only need to publish container ports with -p if you want to access them from your Docker host or another device on your physical network. Docker networks are the preferred alternative approach for inter-container traffic.
Containers that share a network can always communicate with each other, even if their ports have not been explicitly published.
In this example, the web container could connect to a MySQL server running on port 3306 in the database container using the database:3306 address. Docker automatically sets up routing tables for the container names in the network.
Using Port Ranges
Docker can expose and publish entire port ranges when you need to have multiple ports available:
In the first case, –publish-all will assign 100 random ports on your host and map them into the container’s range. The second form explicitly binds a host range to a container range as normal. Performance can be impacted if you use a very large number of ports as an iptables rule will be created for each one.
Summary
Exposed ports are pieces of metadata that define the ports listened to by software inside a container image. The presence of exposed ports doesn’t render them accessible on the host’s interfaces unless you manually publish them. In this sense, the verb “expose” is a misnomer, as many people assume it’s an active action when in fact it’s an informational statement. EXPOSE should be treated as documentation whereas the -p flag creates a functioning port mapping.
Docker does provide some extra behavior based on EXPOSE instructions. You can view a container’s exposed ports with docker ps irrespective of whether they’ve been published. There’s also the –publish-all flag that publishes an image’s exposed ports to random host ports.
A container’s ports are always accessible via its own IP address, even if they’re not published. Any device which can reach the container using its Docker-assigned IP, visible in docker inspect output, will be able to use all its listening ports.
You only need to publish ports when you want to access a container using your host’s network interface. If you’re using Docker networks, port publication isn’t necessary. Communication between containers in the same network is uninhibited, whether or not the ports involved have been exposed or published.