Let’s dockerize a NodeJS app

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. 😃

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.

Terminology

  • 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:
https://www.docker.com/get-started

Linux
If you’re using Linux, read through this manual by the Docker team:
https://docs.docker.com/engine/install

Initializing a new NodeJS project

First, create a new NodeJS project by running

npm init

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>",
"version": "1.0.0",
"description": "<Your description>",
"main": "server.js"
"scripts": {
"start": "node server.js"
},
"author": "<Your name>",
"license": "MIT",
"dependencies": {
"express": "^4.17.1"
}
}

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. 🙄

node server.js

Fire up your app and navigate to http://localhost:80/. You should see a blank page with a text saying “Hello world!”

http://localhost:80/

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.

FROM node:12

Next, we create a new working directory. This is were our server’s source code will be located inside the image.

WORKDIR /usr/src/app

Now only an empty working directory exists, this is why we need to copy all source files into this directory, starting with package.json.

COPY package.json .

Maybe you’re wondering, “why are we copying package.json to the root and not to /usr/src/app?”
Well, by running the WORKDIR command we automatically set the working directory to /usr/src/app for all other commands we run after that.

With 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.

EXPOSE 80

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:

.
├── .dockerignore
├── Dockerfile
├── package-lock.json
├── package.json
└── server.js

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

The -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 Dockerfile.

See 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>!

Well done! Your very first Docker container is now up and running! 👏
Don’t forget that you can find the source code over at GitHub!

If you have Instagram, don’t miss my content on there! 🔥

Hello! 👋 I aim to make understanding JavaScript as easy as possible! Check out my Instagram page too, that's were I post a lot of valuable content! 🧠

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store