When port forward on docker container is not enough?

Have you ever run a server on your local machine, everything worked well and when you deployed it to a container your requests suddenly didn’t get to the server even though everything looks fine with the container logs?

In order to better understand the problem, let's take a look at a live example first. For that, I have prepared an angular application, but we can later generalize the problem to any other language as well.

so let's run the application on our local machine:

> ng serve

After a few seconds we will get the following message which implies the server is up and running:

** Angular Live Development Server is listening on localhost:4200, open your browser on http://localhost:4200/ **
: Compiled successfully.

You can verify that by accessing http://localhost:4200 on your favorite browser.

Now we know our service is working, let's deploy it to a container.

I have prepared the following Dockerfile:

FROM node:12

WORKDIR /app

COPY ./Alguru /app

RUN npm install

ENTRYPOINT [“npm”]

CMD [“run”, “start:client”]

Where “start:client” is an alias for “ng serve”.

Now let's build the image using:

docker build . -t medium_example

And run the container using:

docker run -p 4200:4200 medium_example

We then get this message again:

** Angular Live Development Server is listening on localhost:4200, open your browser on http://localhost:4200/ **
: Compiled successfully.

But when you try to access http://localhost:4200 from your browser, you now get:

So what is happening here and how can we solve it?

By default, the docker container runs in its own Linux Kernel namespace which helps configure different network devices and IPs to different containers.

In my case, as I’m working on windows, docker runs on a Linux VM (but the concepts are the same in other environments as well). So what actually happens is that the server is listening on localhost:4200 inside its namespace — but I fire the request to localhost:4200 on my local machine.

As it’s easier to see in the diagram, there is no connection between my local machine localhost and the container namespace localhost. Hence, the request doesn’t get to its destination.

Why port forwarding is not enough?

When we ran docker run with -p 4200:4200 we forward request from all interfaces on my local machine, to Docker external interface. Our server is listening only on localhost. Hence, we get an empty response.

Solution — listen on all interfaces

In order to listen on all interfaces on the container namespace, in Angular we need to execute ng serve with — host 0.0.0.0, it might be different depending on the language you are working with, but 0.0.0.0 means “listen on all interfaces” regardless of the language you’re working with.

So let’s add the — host flag to out package.json alias command for ng serve:

“start:client”: “ng serve — host 0.0.0.0

You can see in bold what I just added. Let’s rebuild the image and run it, we will see that our server is now listening on all interfaces as we wanted:

** Angular Live Development Server is listening on 0.0.0.0:4200, open your browser on http://localhost:4200/ **
: Compiled successfully.

Open your browser on http://localhost:4200/ — and you will see your application up and running.

Conclusions:

Containers have their own IP addresses regardless of the hosts. Hence, port forwarding alone will not wire your browser with your application, your application needs to listen on the external IP in order to complete the task, and the easiest way to do that is to listen on all interfaces (0.0.0.0).

Cloud Software Engineer at NICE.