Containerized Node server inaccessible with server.listen(port, '127.0.0.1')
I set up a simple Node server in Docker.
Dockerfile
FROM node:latest
RUN apt-get -y update
ADD example.js .
EXPOSE 1337
CMD node example.js
example.js
var http = require('http');
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello World\n'+new Date);
}).listen(1337, '127.0.0.1');
console.log('Server running at http://127.0.0.1:1337/');
Now build the image
$ docker build -t node_server .
Now run in container
$ docker run -p 1337:1337 -d node_server
$ 5909e87302ab7520884060437e19ef543ffafc568419c04630abffe6ff731f70
Verify the container is running and ports are mapped:
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5909e87302ab node_server "/bin/sh -c 'node exa" 7 seconds ago Up 6 seconds 0.0.0.0:1337->1337/tcp grave_goldberg
Now let's attach to the container and verify the server is running inside:
$ docker exec -it 5909e87302ab7520884060437e19ef543ffafc568419c04630abffe6ff731f70 /bin/bash
And in the container command line type:
root@5909e87302ab:/# curl http://localhost:1337
Hello World
Mon Feb 15 2016 16:28:38 GMT+0000 (UTC)
Looks good right?
The problem
When I execute the same curl command on the host (or navigate with my browser to http://localhost:1337) I see nothing.
Any idea why the port mapping between container and host doesn't work?
Things I already tried:
- Running with the
--expose 1337
flag
Your ports are being exposed correctly but your server is listening to connections on 127.0.0.1
inside your container:
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello World\n'+new Date);
}).listen(1337, '127.0.0.1');
You need to run your server like this:
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello World\n'+new Date);
}).listen(1337, '0.0.0.0');
Note the 0.0.0.0 instead of 127.0.0.1.
Adding EXPOSE 1337 to the docker file
EXPOSE
is mandatory if you want to "expose" that port to other containers.
As BMitch comments:
Expose
isn't needed to publish a port or to connect container to container over a shared docker network.
It's metadata for publishing all ports with-P
and inspecting the image/container.
So:
Running with the
--expose 1337
flag
Not exactly: you need to docker run it with -p 1337:1337
You need either:
- build an image with the
EXPOSE
directive in it (used by-P
) -
or run it with the port published on the host
-p 1337:1337
The test curl http://localhost:1337
was done from within the container (no EXPOSE
or publish needed).
If you want it to work from the Linux host, you need EXPOSE+-P
or you need -p 1337:1337
.
Either.
Declaring it expose alone is good for documenting the intent, but does not do anything alone.
For instance:
In that figure, 8080 is EXPOSE'd, published to the Linux host 8888.
And if that Linux host is not the actual host, that same port needs to be fastfowarded to the actual host. See "How to access tomcat running in docker container from browser?".
If localhost does not work from the Linux host, try its IP address:
CID=$(docker run -p 1337:1337 -d node_server)
CIP=$(docker inspect --format '{{ .NetworkSettings.IPAddress }}' ${CID})
curl http://${CIP}:1337
Or, as mentioned above, make your server listen from connections coming from any IP: 0.0.0.0
which is the broadcast address or zero network.