Docker is a great piece of software. It enables us developers to create, deploy and run apps easily by using containers.
Containers are packaged-up apps with all the things they need to run, such as libraries and other dependencies.
The great thing about Docker containers is: they can run on any machine that has Docker installed, regardless of its settings and OS. 😃
“Well, it runs on my machine! I don’t know why it doesn’t on yours!”
This saying is now a thing of the past. With Docker, you won’t run into problems like this.
In this article we’ll take a close look at how to develop a simple Express server with NodeJS and how to package it up into a Docker container.
🔗 You’ll also find the source code over at GitHub.
- Image: A Docker image is like a “snapshot” of a Docker virtual machine at a specific point in time. These are immutable — that means you can’t modify them after creating a new image. Images include all files an application needs to run inside a container: config files, libraries, environment variables and runtimes.
- Tag: A tag is like a version tag for images. You can use them to distinguish between older and newer versions of an image.
- Container: A container is an instance of an image. Think of an image as being a recipe and a container being the cake. Many different applications can run inside an container!
Installing Docker 🐋
Of course, in order to use Docker’s great features you need to install it on your system first. If it’s already installed, you can skip this step!
Windows / Mac
You can download Docker from the official “get started” page:
If you’re using Linux, read through this manual by the Docker team:
Initializing a new NodeJS project
First, create a new NodeJS project by running
Make sure your application’s entry point is set to
server.js, as we’ll call our main file like this.
After you finished initializing your new project, install the Express library.
npm install express --save
Now with your project initialized and Express installed, open
package.json and make sure it looks like this:
"name": "<Your project name>",
"description": "<Your description>",
"start": "node server.js"
"author": "<Your name>",
Great, you’ve finished the first step! 👍
Writing your Express app
Let’s continue by writing the code for our simple Express application. Add a new file called
server.js in your project’s root directory.
You can run it now just to make sure that everything’s fine and no errors occur. It would be a shame if you package up the app and it turns out that it doesn’t even work properly. 🙄
Fire up your app and navigate to
http://localhost:80/. You should see a blank page with a text saying “Hello world!”
No errors? Everything works as intended? Great! We can now move to packaging our simple application into a Docker container! 😎
Adding Docker files
Now, we need to create two files inside the project’s root directory. These are used to tell Docker what to do when building our project.
The first file is simply called
Dockerfile. This is the most important one!
So, what exactly are we instructing Docker to do when building this project? First, we’re telling Docker to use
node with tag
12 as the base image.
Next, we create a new working directory. This is were our server’s source code will be located inside the image.
Now only an empty working directory exists, this is why we need to copy all source files into this directory, starting with
COPY package.json .
Maybe you’re wondering, “why are we copying
package.json to the root and not to
Well, by running the
WORKDIR command we automatically set the working directory to
/usr/src/app for all other commands we run after that.
package.json being inside the working directory, we can install all dependencies for our application. These will be installed in the image!
RUN npm install
Okay, at the moment we only have added
package.json and the application’s dependencies to the Docker image. The server’s source code is still missing, so we have to copy it into the image as well! 👇
COPY . .
The first dot after
COPY refers to the actual project directory on our own computer. The second dot stands for the working directory inside the Docker image.
What we need to do next is to expose port
80, so Express can handle incoming connections and send data back to the clients. All container ports are closed by default, which is why you need to tell Docker explicitly to open a port your app needs.
On the last line, we instruct Docker to start the server process.
CMD [ "node", "server.js" ]
Great, that’s it with the first file! 🔥
But we’re not done yet! Remember there’s a second file we need to create? It’s called
.dockerignore and determines what files and directories should be excluded from the Docker image.
Under no circumstances should
node_modules be included in the image. This would only make the image size unnecessarily huge.
Your project’s structure should now look like this:
Building and running our project
We’re good to go and build the image for this project. After the
-t flag you can set a name for your image.
docker build -t <Your name>/node-express-app .
Depending on how large the project is, it might take a while to build. For small projects this isn’t an issue though! 😅
After Docker’s finished building your project image, you’re able to run it again via command line!
docker run -p 80:80 -d <Your name>/node-express-app
-p flag binds your computer’s port to the container’s exposed port. Your computer’s port can be any port you choose, but the container’s port can be only one you previously exposed inside the
-d flag? It makes sure the container runs in “detached mode”. This means you can still use your console after starting up your container.
If you want to follow the output afterwards, simply run
docker logs -f <Container ID>!