How to Build a Node.js App with Docker

April 21, 2023

Introduction

Node.js is a powerful JavaScript framework for creating web applications, while Docker is a great solution for packing and deploying software. Combining these two technologies helps create a scalable and consistent environment to streamline application deployment.

This article demonstrates how to build a Node.js app with Docker.

How to Build a Node.js App With Docker

Prerequisites

Step 1: Create a Project and Install Dependencies

To create the image, start by making the Node.js project directory and installing dependencies. Follow the steps below:

1. Create a project directory and enter the location:

mkdir my_project && cd my_project

The my_project directory is the root directory of the Node.js application. If you use a different name, change the name in all subsequent commands.

2. Create a package.json file using an editor such as nano:

nano package.json

The file provides a simple way to manage Node.js dependencies and project information.

3. Add the following information to the file:

{
  "name": "nodejs-docker-kb-demo",
  "version": "1.0.0",
  "description": "Node.js Docker KB demonstration",
  "author": "Your Name <yourname@email.com>",
  "license": "MIT",
  "main": "app.js",
  "keywords": ["nodejs", "kb", "docker"],
  "dependencies": {
    "express": "^4.16.4"
  }
}

The file consists of the following fields:

  • name is the project name that gets published to the npm registry. Use a short, descriptive, and unique name.
  • version is the version number of the project.
  • description is a short description of the project.
  • author defines the project creator.
  • license is set to MIT to allow free use and distribution.
  • main defines the project's entry point (an app.js file).
  • keywords are the project's keywords, which npm indexes.
  • dependencies lists the dependent package (express) and version.

4. Save the package.json file and close nano (CTRL+X, Y, Enter).

5. Install the project's dependencies with:

npm install
npm install terminal output

The command installs the packages listed in the dependencies field in the package.json file.

Step 2: Create App Structure

This step creates a simple web application that provides information on phoenixNAP Bare Metal Cloud (BMC) servers. The project directory consists of the following files:

  • The app.js file is the main entry point and contains all project routes.
  • The views directory contains the following static pages for the web application:
    • The index.html file is the landing page with simple BMC information which links to a more detailed page.
    • The bmc.html file is the detailed page with additional information.

Follow the instructions below to create the application structure.

Create Routes and Mount Assets

The following steps create the project routes and mount the project assets. Routing allows linking requests to the correct paths and corresponding pages.

1. In the project directory (~/my_project), create the main entry point file:

nano ~/my_project/app.js

2. Define the project constants. Add the following code to the app.js file:

const express = require('express');
const app = express();
const router = express.Router();
const path = __dirname + '/views/';
const port = 8080;
app.js constants

The code does the following:

  • Line 1 loads the Express module, which the following two lines use to create backend objects.
  • Lines 2-3 create the Express app object, which gives access to create a Router object. The Router object helps define HTTP method routes and how the app handles requests.
  • Line 4 defines the default path for the project's pages as ~/my_project/views/.
  • Line 5 sets the default listening and bind port as 8080.

Keep the file open and proceed to the next step.

3. Add the project routes with the following code:

router.use(function (req,res,next) {
  console.log('/' + req.method);
  next();
});
router.get('/', function(req,res){
  res.sendFile(path + 'index.html');
});
router.get('/bmc', function(req,res){
  res.sendFile(path + 'bmc.html');
});
app.js routes

The code contains three router functions:

  • router.use is a middleware function that logs requests and forwards them to the app routes.
  • router.get defines two app routes as GET requests. The first defines the default route and links it to the index.html page, whereas the second defines the /bmc route and links to the bmc.html page.

4. Mount the assets and launch the application:

app.use(express.static(path));
app.use('/', router);
app.listen(port, function () {
  console.log('Listening on port 8080')
})
app.js mounting

The application listens on port 8080.

5. The completed app.js file looks like the following:

const express = require('express');
const app = express();
const router = express.Router();
const path = __dirname + '/views/';
const port = 8080;

router.use(function (req,res,next) {
  console.log('/' + req.method);
  next();
});
router.get('/', function(req,res){
  res.sendFile(path + 'index.html');
});
router.get('/bmc', function(req,res){
  res.sendFile(path + 'bmc.html');
});

app.use(express.static(path));
app.use('/', router);
app.listen(port, function () {
  console.log('Listening on port 8080')
})

Save the file and close nano.

Create Static Pages

The following steps provide a guideline and templates to create the index.html and bmc.html static pages.

1. Create a views directory inside the project:

mkdir ~/my_project/views

2. Create the landing page (index.html):

nano ~/my_project/views/index.html

3. Add the following code to the file:

<!DOCTYPE html>
<html>
  <head>
    <title>About BMC</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>
    <link href="css/styles.css" rel="stylesheet">
  </head>
  <body>
    <nav class="navbar navbar-default">
      <div class="container-fluid">
        <div class="navbar-header">
          <a class="navbar-brand" href="#">BMC Information</a>
        </div>
        <ul class="nav navbar-nav">
          <li class="active">
            <a href="#">Home</a>
          </li>
          <li>
            <a href="/bmc">BMC</a>
          </li>
        </ul>
      </div>
    </nav>
    <div class="jumbotron">
      <div class="container">
        <h1>Bare Metal Cloud</h1>
        <p>API-Driven Dedicated Servers</p>
        <a class="btn btn-danger btn-lg" href="/bmc">Learn more!</a>
      </div>
    </div>
    <div class="container">
      <h2>Up and Running in 60 sec. Hassle-Free Management. No hidden costs.</h2>
      <p>Setting up an enterprise-grade infrastructure should not be time-consuming and frustrating. With Bare Metal Cloud, you can deploy and manage high-performance physical servers with cloud-like ease and simplicity. No more waiting around for hours for your machine to be provisioned. Spin up pre-configured dedicated servers with a single API call or a couple of clicks. Pay by the hour or leverage monthly or yearly reservations to save up to 30% on your servers.</p>
    </div>
  </body>
</html>

The code contains several Bootstrap elements to create a sample responsive page. The navbar sets the home page as active, and links to the current page and the /bmc route. An additional button on the page links to the /bmc page, too.

The code also contains a linked CSS stylesheet (styles.css).

4. Create the bmc.html page with:

nano ~/my_project/views/bmc.html

5. Add the following code to the file:

<!DOCTYPE html>
<html>
  <head>
    <title>About BMC</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>
    <link href="css/styles.css" rel="stylesheet">
  </head>
  <body>
    <nav class="navbar navbar-default">
      <div class="container-fluid">
        <div class="navbar-header">
          <a class="navbar-brand" href="/">BMC Information</a>
        </div>
        <ul class="nav navbar-nav">
          <li>
            <a href="/">Home</a>
          </li>
          <li class="active">
            <a href="/bmc">BMC</a>
          </li>
        </ul>
      </div>
    </nav>
    <div class="jumbotron">
      <div class="container">
        <h1>Bare Metal Cloud</h1>
        <p>Additional information</p>
      </div>
    </div>
    <div class="container">
      <div class="row">
        <div class="col-lg-4">
          <h2>Run any workload</h2>
          <p>No hypervisor. Leverage the raw power of physical hardware and deploy pre-configured environments in minutes.</p>
        </div>
        <div class="col-lg-4">
          <h2>Automate everything</h2>
          <p>Script out infrastructure management and let automation do the heavy lifting. Focus on coding and releasing great software.</p>
        </div>
        <div class="col-lg-4">
          <h2>Scale at the edge</h2>
          <p>Effortlessly resize your infrastructure across the US, Europe, and Asia and bring your apps and workloads closer to your teams and users.</p>
        </div>
      </div>
    </div>
  </body>
</html>

The navbar changes the active page to BMC and links other navigation bar elements to the home page.

6. Create a separate directory for the CSS file:

mkdir ~/my_project/views/css

7. Make the styles.css file:

nano ~/my_project/views/css/styles.css

8. Add the following code to the file:

html{
	font-family: roboto;
}

.navbar {
	margin-bottom: 0;
}

.jumbotron{
	background: #2B2C7C;
	color: white;
	text-align: center;
}

.container{
	text-align: center;
}

h2{
	color: #2B2C7C;
	font-weight: bold;
}

Save the file and close nano.

Step 3: Run the Application

All files and dependencies for the project are ready. To run the application, do the following:

1. Enable traffic on port 8080. For example, if you use UFW, run:

sudo ufw allow 8080

2. Run the server with:

node ~/my_project/app.js

The terminal shows the server is ready and listening on port 8080.

4. Open a browser and access the page via localhost:

localhost:8080
localhost 8080 nodejs index

The link opens the landing page. Click the button or second BMC page in the navigation bar to access the bmc.html page.

localhost 8080 nodejs bmc

5. To quit the server, press CTRL+C in the terminal.

Step 4: Create Dockerfile

A Dockerfile is a script file that provides instructions to create a Docker image. Create a Dockerfile to specify which pages to include in the application container and define the project's dependencies.

Follow the steps below:

1. Create the Dockerfile:

nano Dockerfile

2. Add the build steps to the file:

FROM node:10-alpine
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", "app.js" ]

Each line does the following:

  • The FROM step creates an official Docker image which is based on Alpine Linux. The distribution base image has a minimal size, which helps keep the Docker image size minimal.
  • The RUN step creates directories for managing app permissions and changes the ownership of these directories to the node user. Leaving ownership to the root user is not recommended.
  • The WORKDIR layer sets the working directory for the app as /home/node/app.
  • The COPY command copies the package.json file (and package-lock.json for npm 5 and greater). Docker's caching mechanism enables rebuilding only when the files update with new changes.
  • The USER step changes to the node user.
  • The second RUN step runs the npm install command.
  • The second COPY step changes the ownership of the code files to the node user.
  • The EXPOSE step exposes port 8080 on the container.
  • The CMD step runs the node app.js command and starts the application.

Save the file and close nano.

3. Create an ignore file:

nano .dockerignore

The ignore file specifies the files and directories that should not be added to the container.

4. Add the following list to the file:

npm-debug.log
node_modules
Dockerfile
.dockerignore

If using Git, add the .git directory and .gitignore files to the list.

Step 5: Build an Image and Run Container

The files are ready to build a Docker image from the Dockerfile and to create a container from the image. Run the Docker commands below to complete the process.

1. Build the image with the docker build command:

docker build -t <dockerhub username>/bmc-nodejs-demo-image .
sudo docker build image terminal output

Add your Docker Hub username to the command.

2. Run the following command to see the built docker image:

sudo docker image

The output lists the created Docker image.

3. Create the container with the docker run command:

sudo docker run --name bmc-nodejs-demo-image -p :8080 -d <dockerhub username>/bmc-nodejs-demo-image
docker run nodejs image terminal output

4. List the container details and fetch the address from the output:

sudo docker ps -a
docker ps ports section terminal output

The PORTS section from the output shows the server address and port.

5. Access the address through a browser to view the containerized image.

Step 6: Publish to Docker Hub

Publishing the image to a Docker Hub repository makes the project available for further development and subsequent use. To publish the project to the Docker Hub, do the following:

1. Log into your Docker Hub account with the following command:

sudo docker login -u <dockerhub username>
docker login terminal output

Enter the password when prompted to log in.

2. Push the image to the Docker Hub with:

sudo docker push <dockerhub username>/bmc-nodejs-demo-image
docker push terminal output

The image is now visible on your Docker Hub page and available for use. The link to the Docker Hub page appears in the terminal output.

3. To pull the image to a different machine, use:

sudo docker pull <dockerhub username>/bmc-nodejs-demo-image

Alternatively, stop the docker container and remove the container and image from the current machine. Pull the image and use the same docker run command from step 5 to build the container.

Conclusion

After going through the steps in this guide, you know how to build a Node.js app with Docker.

Next, read up on Docker container best practices.

Was this article helpful?
YesNo
Milica Dancuk
Milica Dancuk is a technical writer at phoenixNAP who is passionate about programming. Her background in Electrical Engineering and Computing combined with her teaching experience give her the ability to easily explain complex technical concepts through her content.
Next you should read
How to Fix Docker Permission Denied?
October 27, 2022

The "Permission Denied" error in Docker occurs when a non-root user lacking sufficient privileges attempts to execute a Docker command. This...
Read more
10 Docker Security Best Practices
September 14, 2020

This article provides 10 container security tips that can help you prevent attacks and privilege breaches. It explains the importance of keeping containers updated, privileges...
Read more
How to Deploy and Run Redis in Docker
July 23, 2020

The tutorial shows you how to deploy Redis using the Docker run command. Also, learn how to deploy Redis on multiple servers in your cluster, efficiently link...
Read more
How to SSH into a Running Docker Container and Run Commands
December 19, 2023

This knowledge base article explains how to SSH into a running Docker container. Docker exec and docker attach commands are...
Read more