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.
Prerequisites
- A system running Ubuntu (the guide uses Ubuntu 22.04).
- Access to an administrator account.
- Docker installed.
- Node.js and npm installed.
- A Docker Hub account.
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
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;
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');
});
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 theindex.html
page, whereas the second defines the/bmc
route and links to thebmc.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')
})
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
The link opens the landing page. Click the button or second BMC page in the navigation bar to access the bmc.html page.
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 thenpm 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 thenode 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 .
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
4. List the container details and fetch the address from the output:
sudo docker ps -a
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>
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
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.