What is the difference between "expose" and "publish" in Docker?
Asked Answered
I

8

749

I'm experimenting with Dockerfiles, and I think I understand most of the logic. However, I don't see the difference between "exposing" and "publishing" a port in this context.

All the tutorials I have seen first include the EXPOSE command in the Dockerfile:

...
EXPOSE 8080
...

They then build an image from this Dockerfile:

$ docker build -t an_image - < Dockerfile

And then publish the same port as above when running the image:

$ docker run -d -p 8080 an_image

or publish all ports using

$ docker run -d -P an_image

What is the point of exposing a port in the Dockerfile, if it will be published anyway? Would there ever be a need to expose a port first, and not publish it later? Effectively, I would like to specify all the ports that I will use in the Dockerfile when creating the image, and then not bother with them again, running them simply with:

$ docker run -d an_image

Is this possible?

Inform answered 1/3, 2014 at 6:35 Comment(0)
B
956

Basically, you have three (four) options:

  1. Neither specify EXPOSE nor -p
  2. Only specify EXPOSE
  3. Specify EXPOSE and -p
  4. Only specify -p which implicitly does EXPOSE
  1. If you specify neither EXPOSE nor -p, the service in the container will only be accessible from inside the container itself.

  2. If you EXPOSE a port, the service in the container is not accessible from outside Docker, but from inside other Docker containers. So this is good for inter-container communication.

  3. If you EXPOSE and -p a port, the service in the container is accessible from anywhere, even outside Docker.

  4. If you do -p, but do not EXPOSE, Docker does an implicit EXPOSE. This is because if a port is open to the public, it is automatically also open to other Docker containers. Hence -p includes EXPOSE. This is effectively same as 3).

The reason why both are separated is IMHO because:

  • choosing a host port depends on the host and hence does not belong to the Dockerfile (otherwise it would be depending on the host),
  • and often it's enough if a service in a container is accessible from other containers.

The documentation explicitly states:

The EXPOSE instruction exposes ports for use within links.

It also points you to how to link containers (legacy feature), which basically is the inter-container communication I talked about.

Bumpkin answered 3/3, 2014 at 15:2 Comment(17)
I think that you are not correct with EXPOSE. From other containers, you can access all container ports without exposing them. I tried that. The catch here is that container IP address is unpredictable. I believe that link is used to specify which container you want to connect (so you link to specific container IP), not to enable connection.Keek
yes, @jiri is right. I think the meaning of EXPOSE instruction is set the environment variable automatically by docker when the container being linked by others which can access the container by variable.Ruperto
"If you do not specify any of those", would be useful if you clarified that with "those" you mean EXPOSE and -p and not the the three bullet points precedented. Got me confused there a bit.Grandnephew
As my test, If you do not specify any of those, the service in the container WILL BE accessible from docker host and other container by the container's IP address, but not accessible from outeside the docker host. I am using Docker Toolbox 1.12.0-rc3 in Mac OS XFrere
To be fully complete, this answer should also address a fourth possible case: you didn't specify EXPOSE, but you did specify -p. It's my understanding that if you always use -p and run individual containers then EXPOSE is fine to omit, but it becomes useful/necessary when using -P or --link. (And since you don't know how other people will use your image, EXPOSE should be specified in any public images.)Inaccuracy
so you can't say in a dockerfile : " hey I want the host port to be 80 " ? It would make sens for a serverPriory
Exactly: You can't. The reason for this is that you don't know in advance what else will be run on the host, so it does not make any sense to hard-code this.Bumpkin
The docs no longer state "The EXPOSE instruction exposes ports for use within links".Notional
Downvote because this is substantially incorrect. Expose is basically documentation, and not using it does not restrict access. This is a dangeros misunderstanding if anyone relies on it to limit access.Drawers
it seems like your answer is outdated. it might be helpful to edit the answer to point out this fact and link to @tgogos answer below (at the time of writing this comment), link: #22111560Badderlocks
would you possibly mention an example of both -p and EXPOSE command ? Let's say we have: docker run -d --name maria -e 'root' -p 3306:3306 mysql/mysql-serverMiddlings
generally, what is the best practice to pass a switch for exposing a service to anywhere outside Docker ?Middlings
Not to beat a dead horse but I am wondering why this is still the accepted answer when, like others have stated, this isn't correct. You can absolutely map a host port to a running container's port by specifying -p when running the image, with or without the EXPOSE keyword in the Dockerfile.Guertin
I tried to edit the post to let people know that, as @Drawers pointed, this answer is not correct but I couldn't because this post already has too many edits pending to be reviewed. I had no choice but to flag this answer to bring more attention to moderators to edit the post by themselves.Minimal
Downvote this because it is chosed as accepted but the answer will confuse people.Autotransformer
Can I actually expose a port which is already used and published by another container?Isidora
Downvote because option 2 is not correctly explained. The correct answer is belowNikos
G
381

Short answer:

  • EXPOSE is a way of documenting
  • --publish (or -p) is a way of mapping a host port to a running container port

Notice below that:

  • EXPOSE is related to Dockerfiles ( documenting )
  • --publish is related to docker run ... ( execution / run-time )

Exposing and publishing ports

In Docker networking, there are two different mechanisms that directly involve network ports: exposing and publishing ports. This applies to the default bridge network and user-defined bridge networks.

  • You expose ports using the EXPOSE keyword in the Dockerfile or the --expose flag to docker run. Exposing ports is a way of documenting which ports are used, but does not actually map or open any ports. Exposing ports is optional.

  • You publish ports using the --publish or --publish-all flag to docker run. This tells Docker which ports to open on the container’s network interface. When a port is published, it is mapped to an available high-order port (higher than 30000) on the host machine, unless you specify the port to map to on the host machine at runtime. You cannot specify the port to map to on the host machine when you build the image (in the Dockerfile), because there is no way to guarantee that the port will be available on the host machine where you run the image.

from: Docker container networking

Update October 2019: the above piece of text is no longer in the docs but an archived version is here: docs.docker.com/v17.09/engine/userguide/networking/#exposing-and-publishing-ports

Maybe the current documentation is the below:

Published ports

By default, when you create a container, it does not publish any of its ports to the outside world. To make a port available to services outside of Docker, or to Docker containers which are not connected to the container's network, use the --publish or -p flag. This creates a firewall rule which maps a container port to a port on the Docker host.

and can be found here: docs.docker.com/config/containers/container-networking/#published-ports

Also,

EXPOSE

...The EXPOSE instruction does not actually publish the port. It functions as a type of documentation between the person who builds the image and the person who runs the container, about which ports are intended to be published.

from: Dockerfile reference






Service access when EXPOSE / --publish are not defined:

At @Golo Roden's answer it is stated that::

"If you do not specify any of those, the service in the container will not be accessible from anywhere except from inside the container itself."

Maybe that was the case at the time the answer was being written, but now it seems that even if you do not use EXPOSE or --publish, the host and other containers of the same network will be able to access a service you may start inside that container.

How to test this:

I've used the following Dockerfile. Basically, I start with ubuntu and install a tiny web-server:

FROM ubuntu
RUN apt-get update && apt-get install -y mini-httpd

I build the image as "testexpose" and run a new container with:

docker run --rm -it testexpose bash

Inside the container, I launch a few instances of mini-httpd:

root@fb8f7dd1322d:/# mini_httpd -p 80
root@fb8f7dd1322d:/# mini_httpd -p 8080
root@fb8f7dd1322d:/# mini_httpd -p 8090

I am then able to use curl from the host or other containers to fetch the home page of mini-httpd.


Further reading

Very detailed articles on the subject by Ivan Pepelnjak:

Gradation answered 1/12, 2017 at 13:20 Comment(4)
this is now the correct answer. accepted answer is based on prior versions, it seems.Lacreshalacrimal
EXPOSE does actually do something and is not merely documentation. If you run with the -P flag, it publishes "all exposed ports to random ports".Wavelength
The section on not publishing ports at all is wrong. I've just tried that and it just doesn't work and I wouldn't expect it to work. I tried something similar in compose too, using service names on the same network, and that also doesn't work. Also, as per papiro's comment, EXPOSE does have an effect with -P.Countrywide
The section: Service access when EXPOSE / --publish are not defined is still valid and works as described above (I just tested it again). Maybe you tried it on docker for Windows or Mac (where networking is more complex)? On a Linux VM it works exactly as it's written, the example is complete and verifiable.Gradation
H
15

See the official documentation reference: https://docs.docker.com/engine/reference/builder/#expose

The EXPOSE allows you to define private (container) and public (host) ports to expose at image build time for when the container is running if you run the container with -P.

$ docker help run
...
  -P, --publish-all                    Publish all exposed ports to random ports
...

The public port and protocol are optional, if not a public port is specified, a random port will be selected on host by docker to expose the specified container port on Dockerfile.

A good pratice is do not specify public port, because it limits only one container per host ( a second container will throw a port already in use ).

You can use -p in docker run to control what public port the exposed container ports will be connectable.

Anyway, If you do not use EXPOSE (with -P on docker run) nor -p, no ports will be exposed.

If you always use -p at docker run you do not need EXPOSE but if you use EXPOSE your docker run command may be more simple, EXPOSE can be useful if you don't care what port will be exposed on host, or if you are sure of only one container will be loaded.

Hilar answered 8/5, 2015 at 14:46 Comment(3)
this is correct. When you have EXPOSE portNumber in Dockerfile, remember to invoke docker run with -P.Pandarus
I'm wishing the language in this post was a bit more clear for those like me newer to some of the concepts.Vallation
BBaysinger, What concept you want to be clarify ?Hilar
H
12

You expose ports using the EXPOSE keyword in the Dockerfile or the --expose flag to docker run. Exposing ports is a way of documenting which ports are used, but does not actually map or open any ports. Exposing ports is optional.

Source: github commit

Hoarfrost answered 15/11, 2017 at 2:54 Comment(2)
poor wording choice imo? if it's just documenting, they could've chosen... i dont know, anything BUT expose lol.Oudh
So using --expose PORT in CLI really does nothing? I would understand its place in Dockerfile, but why make it a cli flag?Loney
S
6

Most people use docker compose with networks. The documentation states:

The Docker network feature supports creating networks without the need to expose ports within the network, for detailed information see the overview of this feature).

Which means that if you use networks for communication between containers you don't need to worry about exposing ports.

Stated answered 28/7, 2017 at 7:30 Comment(0)
N
1

EXPOSE keyword lets owner to inform others that which ports are going to be used by the container mainly.

You can publish any port even if you don't specify the port in EXPOSE.

For example we create a Dockerfile with nginx image that exposes port 1234

FROM nginx:latest
EXPOSE 1234

and build it

docker build -t porttest .

And run it with publishing 80 port to localhost:80

docker run -p 80:80 porttest

When you go localhost:80, you will see nginx default page. Nginx default page

Negative answered 22/3, 2022 at 7:30 Comment(1)
But unfortunately, that is no answer to the question whether there is a way to make use of the port that's actually exposed in the image WITHOUT explicitly publishing it during container-creation. The answer would be: "yes, you can use -P option of 'docker run' to publish all the ports EXPOSEd in the Dockerfile"Gardner
B
0
  1. expose - will only allow that specific port to connect with container and it will use as "inter container communication " only
  2. -p ( publish ) will map the host port with container port ( which you have already exposed in step 1 or in docker file ) and once you expose it , you will need to map it so we use publish , it will then access container outside world/internet.
Baziotes answered 22/11, 2022 at 16:22 Comment(0)
G
-7

EXPOSE is used to map local port container port ie : if you specify expose in docker file like

EXPOSE 8090

What will does it will map localhost port 8090 to container port 8090

Gillie answered 21/8, 2019 at 11:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.