Exposing a Port in Docker, What Does It Do?
Tags: node, docker, devops, linux
Published: 2020-02-23 12:21:02

*This article was originally published a day earlier at https://maximorlov.com/exposing-a-port-in-docker-what-does-it-do/*

How good is your understanding of what the `EXPOSE` instruction does in a Dockerfile?

Do you know the difference between "expose" and "publish"?

Getting two containers to talk to each other should be a simple task, and yet, as a newbie to the Docker ecosystem, this is harder than it seems.

Do you have to use `EXPOSE` or `-p`? Or maybe both?

Bridges, interfaces, ports, tunnelling... Networking is hard.

What if — instead of guessing at the problem, and hoping you'll magically find a solution — you can confidently make two containers talk to each other.

By the end of this article, you'll understand the difference between exposing a port and publishing a port. You'll find out why `EXPOSE` doesn't lead to the result you want, and what else you can use it for.

## What EXPOSE doesn't do
Let's start by getting the most common misconceptions out of the way.

### Makes a container accessible from the public network ❌
Exposing a port on the container doesn't make it accessible from the public network.

To be able to reach a container from the public network¹, you have to *publish* its port(s) to the host network with the `-p` (or `--publish`) flag.

Exposing a port *won't* publish any ports. If you have a web server listening on port 80, you still need to make sure to publish and map the port to a port on the host network.

You can only publish ports at container run time. Docker will then check if the specified ports on the host are available. As an image builder, you don't know which ports will be available on the host, so it doesn't make sense to specify that in a Dockerfile.

If a published port isn't exposed, Docker will automatically expose the port for you. A published port is by definition an exposed port. The reverse isn't true — an exposed port isn't always a published port.

¹ If you're using Docker Desktop for Mac/Windows, and want to connect to the container from the host, you also need to publish and map the ports. Because Docker uses a VM on these operating systems, the docker0 bridge network isn't visible to the hosts. On Linux, containers are accessible from the host without the need to publish their ports.

### Allows communication between containers ❌
Pop quiz! Container A and container B are running on the same bridge network. Container A exposes port 80 and container B doesn't expose any ports. What type of communication is possible between the two containers?

A) A cannot talk to B, and B cannot talk to A
B) A can talk to B, but B cannot talk to A
C) A cannot talk to B, but B can talk to A
D) A can talk to B, and B can talk to A

Go ahead. I'll wait.

.
.
.
.
.

If you answered C, you lost. The right answer is **D**.

Both containers can talk to each other, regardless whether they expose any ports or not.

That's because containers connected to the same bridge network automatically open *all ports* to each other. This is true for the default bridge network provided by Docker, and for any user-defined bridge network with the default settings².

Exposing a port makes no difference in this case.

If you're trying to figure out why your web server cannot communicate with the database, you can discard this as a possible issue. Better yet, if you exposed a port in your DB container hoping that your web server can make a connection, get rid of it now to avoid confusion. Instead, you might want to try these [4 troubleshooting steps](https://maximorlov.com/4-reasons-why-your-docker-containers-cant-talk-to-each-other/).

² When you create a network, you can pass additional settings to the bridge network driver. One such option is Inter Container Connectivity, which — as you guessed — configures whether containers should have their ports open to each other. This option is enabled by default, but you can disable it.

### Impacts the security of your application ❌
It's easy to think that if you don't expose a port, a container is more secure. No exposed ports means a smaller attack surface right?

Not exactly.

Remember from the previous example that exposing a port is *not* the same as opening it. An exposed port doesn't automatically mean it will be open at runtime. The reverse is also true — for a port to be open, it *doesn't* have to be exposed at build time.

The networking capabilities of a container are established at run time, instead of build time. The person who built the image has no control over the network in which the container will run. It's the responsibility of the person running the container to secure it from the outside world.

Therefore, not using `EXPOSE` doesn't restrict access to a container. You shouldn't rely on it to make it more secure.

## What EXPOSE does
If `EXPOSE` doesn't do any of the above things, then what *does* it do?

### Provides documentation ✅
`EXPOSE` is a way of providing documentation to an image. The instruction inside a Dockerfile is part of the image metadata.

`EXPOSE` is how the person who built the image communicates to the person who will run the container, which port the service inside the container will listen to. The person running the container then knows which ports to publish.

You can view the configuration and metadata for an image or container with `docker inspect`. When ports are exposed, they show up inside the configuration JSON. If any tools or scripts rely on that, exposing a port is a useful way to have them pick that information up.

As an image consumer, this information can be useful in certain situations. When you pull a database image (Redis, PostgreSQL, etc.) from the registry, you can inspect it and find out which ports you need to publish.

### Tells Docker which ports to publish when the -P flag is used ✅
If you run a container with the `-P` (or `--publish-all`) flag, Docker will grab all exposed ports and map them to random high-order ports on the host.

The `-P` flag can be useful when you don't want to poke into the image to figure out what ports you need to publish. To find out which ports Docker has picked at random, you can run `docker port` or `docker ps`.

As far as I know, this is the only use case for `EXPOSE` where it does something practical instead of only providing documentation.

## Final thoughts
The verb "expose" can be confusing and misleading in what it does. Intuitively, when I hear a port is exposed, I think that port is open and the container is accessible to the outside world. That's not true.

By now, you know that exposing a port doesn't do much in a practical sense. When most people talk about making a container accessible from the host, they mean publishing a port.

You know that all it takes for two containers to be able to talk to each other is to connect them to the same bridge network.

If you were using `EXPOSE` for the things it *doesn't* do, then I encourage you to open up your Dockerfiles now and remove it. Save your future self and others from unnecessary confusion.

## Become a skilled Node.js developer

Every other Tuesday I send an email with tips on building solid Node.js applications. If you want to grow as a web developer and move your career forward with me, [drop your email here 💌](https://maximorlov.ck.page/0076aaf499).