Can connect to Container without EXPOSEing ports

Should containers in the default bridge network allow connections to host and other containers attached to this same network without EXPOSEing ports?

  • Created a container with web service listening on port 8787.
  • Failed to reveal any port through either Dockerfile EXPOSE command or docker run --expose.
  • docker run command properly attached the Container to default bridge network.
  • Neither docker ports nor docker ps reported any exposed ports for the involved containers.
  • Ran Chrome instance on Docker Engine Host and specified Container’s IP followed by 8787 ( Server replied with its logon page. However, I didn’t expect it to because its port wasn’t EXPOSEd.

According to the legacy bridge --link documentation:

So what does linking the containers actually do? You’ve learned that a link allows a source container to provide information about itself to a recipient container. In our example, the recipient, web, can access information about the source db. To do this, Docker creates a secure tunnel between the containers that doesn’t need to expose any ports externally on the container; you’ll note when we started the db container we did not use either the -P or -p flags. That’s a big benefit of linking: we don’t need to expose the source container, here the PostgreSQL database, to the network.

When I first read the above I thought the phrase secure tunnel between containers implied that only the link’s recipient container, within the bridge network, could communicate to the link’s source container. Other containers on the bridge network or host itself couldn’t directly address (communicate) with the source container.

However, after reflecting on this post and running though some container networking scenarios, the word network in the phrase: here the PostgreSQL database, to the network. at the paragraph’s tail, doesn’t refer to the default bridge network but to the IP network external to the Docker Engine Host. In other words, the network that Docker Engine is itself considered as a host.

Given this new understanding EXPOSE:

  • purely documents (doesn’t enforce) which ports of a container can be mapped to Docker Engine Host’s ports, permitting certain convenience features like -P, -p and
  • doesn’t specify the port(s) that Containers must bind to in order to enable communication between other containers.

In other words, containers can freely communicate to one another over any port they wish, without having to declare exposed ports, as long as they haven’t been intentionally isolated.

When I began this post, I wasn’t confident that the understanding arrived at by the post’s end is correct. But now I’m confident that it is but would appreciate someone more knowledgeable to comment, especially if my latest understanding is flawed.


If you can both find and reach the container’s private IP address, then it works as you described it. Docker doesn’t make it trivial to find this address, and it’s on a host-private network, so this isn’t very useful from other hosts.

The thing you’re actually looking for is that http://localhost:8787/ goes nowhere; but if you docker run -p 8787:8787, it will get routed into your container.

That’s almost right. docker run -p doesn’t require the ports to be exposed; exposing ports does affect what ports docker run -P will publish. I think it used to be the case that the (only available) container network was pretty locked down and containers were isolated even from each other, but I think that rule’s been relaxed.

In practice, I think EXPOSE is almost only documentation.

1 Like