Need help?

Our experts have had an average response time of 13.14 minutes in February 2024 to fix urgent issues.

We will keep your servers stable, secure, and fast at all times for one fixed price.

Node.js Application with Docker on Ubuntu 20.04 – How to build?

by | Feb 22, 2021

Want to know how to build Node.js Application with Docker on Ubuntu? We can help you.

Docker allows us to run applications as containers, offering a lighter-weight alternative to virtual machines.

As part of our Docker Hosting Support, we assist our customers with several Node.js queries.

Today, let us see how our Support Techs build a Node.js Application with Docker on Ubuntu 20.04

 

Node.js Application with Docker on Ubuntu

In this article, let us create an application image. We will then build a container and push it to Docker Hub for future use. Finally, we will pull the image from the Docker Hub repository and build another container. Hence demonstrating how we can recreate and scale our application.

Before we begin, our Support Techs suggest having:

  • A Ubuntu 20.04 server with non-root Sudo user with key-based authentication
  • Docker on the server
  • Node.js and npm
  • A Docker Hub account

 

How to build a Node.js Application with Docker on Ubuntu

The starting point is typically creating an image for our application, which we can then run in a container. The image includes our application code, libraries, configuration files, environment variables, and runtime.

Moving ahead, let us see in detail how our Support Techs build a Node.js Application with Docker on Ubuntu 20.04.

  • Step 1 – Install Application Dependencies

To create our image, we need to make our application files.

First, create a directory in the non-root user’s home directory, and navigate to that directory:

$ mkdir node_project
$ cd node_project

Then, create a package.json file with the project’s dependencies and other information about the project.

Npm recommends a short and descriptive project name and to avoid duplicates in the npm registry. Mention the MIT license in the license field, permitting the free use and distribution of the application code.

~/node_project/package.json

{
“name”: “docker_web_app”,
“version”: “1.0.0”,
“description”: “Node.js on Docker”,
“author”: “First Last ”,
“main”: “server.js”,
“scripts”: {
“start”: “node server.js”
},
“dependencies”: {
“express”: “^4.16.1”
}
}

Eventually, save and close the file.

To install our project’s dependencies, we run:

$ npm install

This will install the packages we have listed in our package.json file. If we use npm version 5 or later, this will generate a package-lock.json file and copy it to our Docker image.

  • Step 2 – Create the Application Files

Create a server.js file that defines a web app using the Express.js framework:

‘use strict’;

const express = require(‘express’);

// Constants
const PORT = 8080;
const HOST = ‘0.0.0.0’;

// App
const app = express();
app.get(‘/’, (req, res) => {
res.send(‘Hello World’);
});

app.listen(PORT, HOST);
console.log(`Running on http://${HOST}:${PORT}`);
  • Step 3 – Write the Dockerfile

Using a Dockerfile allows us to define our container environment and avoid discrepancies with dependencies or runtime versions.

Initially, in the project’s root directory, we create the Dockerfile:

$ nano Dockerfile

Our first step is to add the base image for our application that will form the starting point of the application build.

Consider the node:12 image. Add the following FROM instruction to set the application’s base image:

~/node_project/Dockerfile

FROM node:12

This image includes Node.js and npm.

By default, the Docker Node image includes a non-root node user. We will use the node user’s home directory as the working directory and set it as our user inside the container.

To fine-tune the permissions on our application code in the container, let us create the node_modules subdirectory in /home/node along with the app directory.

In addition, we will set ownership on them to our node user:

~/node_project/Dockerfile

…
RUN mkdir -p /home/node/app/node_modules && chown -R node:node /home/node/app

Then, we set the working directory of the application to /home/node/app:

~/node_project/Dockerfile

…
WORKDIR /home/node/app

If a WORKDIR is not set, Docker will create one by default, so it is a good idea to set it explicitly.

Next, copy the package.json and package-lock.json (for npm 5+) files:
~/node_project/Dockerfile

…
COPY package*.json ./

Adding this COPY instruction allows us to take advantage of Docker’s caching mechanism.

To ensure that all of the application files are owned by the non-root node user, we switch the user to the node before running npm install:

~/node_project/Dockerfile

…
USER node

Once done, we can run npm install:

~/node_project/Dockerfile

…
RUN npm install

Next, we copy the application code with the appropriate permissions to the application directory on the container:

~/node_project/Dockerfile

…
COPY –chown=node:node . .

Finally, we expose port 8080 on the container and start the application:

~/node_project/Dockerfile

…
EXPOSE 8080

CMD [ “node”, “server.js” ]

The complete Dockerfile looks like this:

~/node_project/Dockerfile

FROM node:12
RUN mkdir -p /home/node/app/node_modules && chown -R node:node /home/node/app
WORKDIR /home/node/app
COPY package*.json ./
USER node
RUN npm install
COPY –chown=node:node . .
EXPOSE 8080
CMD [ “node”, “server.js” ]

Eventually, save and close the file.

Before building the application image, let us add a .dockerignore file:

$ nano .dockerignore

Inside the file, add local node modules, npm logs, Dockerfile, and .dockerignore file:

~/node_project/.dockerignore

node_modules
npm-debug.log
Dockerfile
.dockerignore

If we are working with Git then we will also want to add our .git directory and .gitignore file.

Eventually, save and close the file.

The -t flag with docker build will allow us to tag the image with a memorable name. Since we are going to push the image to Docker Hub, we will tag the image as nodejs-image-demo.

Make sure to also replace our_dockerhub_username with our own Docker Hub username:

$ sudo docker build -t our_dockerhub_username/node-web-app .

The . specifies that the build context is the current directory.

It will take a minute or two to build the image. Once done, check our images:

$ sudo docker images

Our output will be as follows:

Output

REPOSITORY TAG ID CREATED
node 12 1934b0b038d1 5 days ago
/node-web-app latest d64d3505b0d2 1 minute ago

To build the container we run:

$ sudo docker run –name node-web-app -p 80:8080 -d our_dockerhub_username/node-web-app

Once done, we can inspect a list of our running containers with docker ps:

$ sudo docker ps
Output
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e50ad27074a7 our_dockerhub_username/node-web-app “node server.js” 8 seconds ago Up 7 seconds 0.0.0.0:80->8080/tcp node-web-app

We can now visit our application by navigating our browser to our server IP without the port:

http://our_server_ip
  • Step 4 – Using a Repository to Work with Images

The first step to pushing the image is to log in to the Docker Hub account.

$ sudo docker login -u our_dockerhub_username

Logging in this way will create a ~/.docker/config.json file in our user’s home directory with our Docker Hub credentials.

Then we can now push the application image to Docker Hub:

$ sudo docker push our_dockerhub_username/node-web-app

Let us test the utility of the image registry by destroying our current application container and image. We will then rebuild them with the image in our repository.

First, list the running containers:

$ sudo docker ps
Output
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e50ad27074a7 our_dockerhub_username/node-web-app “node server.js” 3 minutes ago Up 3 minutes 0.0.0.0:80->8080/tcp node-web-ap

Using the CONTAINER ID listed in the output, stop the running application container:

$ sudo docker stop e50ad27074a7
List all the images with the -a flag:
$ docker images -a
Output
REPOSITORY TAG IMAGE ID CREATED SIZE
our_dockerhub_username/nodejs-image-demo latest 1c723fb2ef12 7 minutes ago 73MB
  2e3267d9ac02 4 minutes ago 72.9MB
  8352b41730b9 4 minutes ago 73MB
  5d58b92823cb 4 minutes ago 73MB
  3f1e35d7062a 4 minutes ago 73MB
  02176311e4d0 4 minutes ago 73MB
  8e84b33edcda 4 minutes ago 70.7MB
  6a5ed70f86f2 4 minutes ago 70.7MB
  776b2637d3c1 4 minutes ago 70.7MB
node 12 f09e7c96b6de 3 weeks ago 70.7MB

We can remove the stopped container and all of the images with the following command:

$ docker system prune -a

Type y when prompted in the output to confirm. Make note that this will also remove the build cache.

With all of the images and containers deleted, we can now pull the application image from Docker Hub:

$ docker pull our_dockerhub_username/node-web-app

List the images once again:

$ docker images
Output
REPOSITORY TAG IMAGE ID CREATED SIZE
our_dockerhub_username/nodejs-image-demo latest 1c723fb2ef12 11 minutes ago 73MB

We can now rebuild the container.

$ docker run –name node-web-app -p 80:8080 -d our_dockerhub_username/node-web-app

Then, list the running containers:

$ docker ps
Output
CONTAINER ID IMAGE COMMAND CREATED
STATUS PORTS NAMES
f6bc2f50dff6 our_dockerhub_username/nodejs-image-demo “node server.js” 4 seconds ago
Up 3 seconds 0.0.0.0:80->8080/tcp node-web-app

Finally, visit http://our_server_ip once again to view the running application.

 

Few common errors

  • node: not found

Recently one of our customers received the following error while running dockerfile that tries to run server.js:

sh: 1: node: not found

This might be due to the way node.js was installed from the Ubuntu repository. Ubuntu repository usually serves pretty old versions of packages and the whole node/nodejs package naming problem is pretty confusing.

In order to avoid this, rather than using the Ubuntu repository, use one of the official node images from the Docker repository.

However, if we go ahead with our own Ubuntu-based image with node.js, look at installing node.js directly from the source.

To get shell access to the container:

docker run -it –rm  /bin/bash

Once we run this command on the host, we will have a new bash shell prompt. Now we have shell access to a temporary container based on our image.

Try node –version or nodejs –version to see if it is installed. If that works, try which node or which nodejs to find the path to the node binary.

If we can find the binary, we can edit our Dockerfile to include a link from somewhere in our path to that binary.

For example, assuming which nodejs gives /usr/bin/nodejs, we can use the link in our Dockerfile:

RUN ln -s /usr/bin/nodejs /usr/bin/node
  • npm: command not found

When we run the container and open a bash shell, executing the command npm -V we may come across:

root@server:/# npm -v
bash: npm: command not found

root@server:/# nvm ls
N/A
node -> stable (-> N/A) (default)
iojs -> N/A (default)

In order to solve this install Node without using the nvm tool:

# curl -sL https://deb.nodesource.com/setup_7.x | bash
# apt-get install -y nodejs

Now, while logging in to the container, it can find the Node executable.

[Need help with the procedures? We’d be happy to assist]

 

Conclusion

In this article, we saw how to build a Node.js Application with Docker on Ubuntu. Our Support Techs has given a detailed description of an effective way they employ.

PREVENT YOUR SERVER FROM CRASHING!

Never again lose customers to poor server speed! Let us help you.

Our server experts will monitor & maintain your server 24/7 so that it remains lightning fast and secure.

GET STARTED

var google_conversion_label = "owonCMyG5nEQ0aD71QM";

0 Comments

Submit a Comment

Your email address will not be published. Required fields are marked *

Categories

Tags

Privacy Preference Center

Necessary

Necessary cookies help make a website usable by enabling basic functions like page navigation and access to secure areas of the website. The website cannot function properly without these cookies.

PHPSESSID - Preserves user session state across page requests.

gdpr[consent_types] - Used to store user consents.

gdpr[allowed_cookies] - Used to store user allowed cookies.

PHPSESSID, gdpr[consent_types], gdpr[allowed_cookies]
PHPSESSID
WHMCSpKDlPzh2chML

Statistics

Statistic cookies help website owners to understand how visitors interact with websites by collecting and reporting information anonymously.

_ga - Preserves user session state across page requests.

_gat - Used by Google Analytics to throttle request rate

_gid - Registers a unique ID that is used to generate statistical data on how you use the website.

smartlookCookie - Used to collect user device and location information of the site visitors to improve the websites User Experience.

_ga, _gat, _gid
_ga, _gat, _gid
smartlookCookie
_clck, _clsk, CLID, ANONCHK, MR, MUID, SM

Marketing

Marketing cookies are used to track visitors across websites. The intention is to display ads that are relevant and engaging for the individual user and thereby more valuable for publishers and third party advertisers.

IDE - Used by Google DoubleClick to register and report the website user's actions after viewing or clicking one of the advertiser's ads with the purpose of measuring the efficacy of an ad and to present targeted ads to the user.

test_cookie - Used to check if the user's browser supports cookies.

1P_JAR - Google cookie. These cookies are used to collect website statistics and track conversion rates.

NID - Registers a unique ID that identifies a returning user's device. The ID is used for serving ads that are most relevant to the user.

DV - Google ad personalisation

IDE, test_cookie, 1P_JAR, NID, DV, NID
IDE, test_cookie
1P_JAR, NID, DV
NID
hblid

Security

These are essential site cookies, used by the google reCAPTCHA. These cookies use an unique identifier to verify if a visitor is human or a bot.

SID, APISID, HSID, NID, PREF
SID, APISID, HSID, NID, PREF