Open In App

Docker Image Optimization for Node.js

Last Updated : 12 Jul, 2024
Comments
Improve
Suggest changes
Like Article
Like
Report

Docker is the most widely containerization tool that developers to deploy applications to production with little to no downtime. In this article, we are going to learn how Docker images can be optimized for Node.js applications for better performance, stability, and security.

Prerequisites

  1. Docker installed
  2. Basic knowledge of Node.js

What is Docker Image

We know a container is an isolated environment, but from where container get files and configuration? A container image is a standardized package that includes all of the files, binaries, libraries, and configurations to run a container.

So, a Docker Image is basically the immutable blueprint that includes all the instructions needed to create the Docker Container.

Why Docker Image Optimization Matters

  1. Optimized images are smaller in size take fewer resources to build and reduce the time required for the build
  2. Optimized images require less CPU resources, disk I/O, and memory, so they perform better.
  3. Optimized images are easier to maintain as they are less complex and focus more on components.
  4. Could services charge for CPU and memory usage, so optimized images reduce the cost of running the service?

How to create a Docker Image

We can get many Docker images from the Docker hub but most of the time we need to make our own custom images. There are two steps for creating a Docker Image-

Steps To Write a Dockerfile

The Dockerfile includes instructions for the Docker Image. Docker reads the Dockerfile and automatically creates image. So common Dockerfile commands are

  • FROM: A valid Dockerfile always starts with it(with only exception of ARG). This command creates a new build stage of the image.
  • COPY: Copies files from previous stages or host OS.
  • WORKDIR: Specifies working directory.
  • EXPOSE: Exposed port of container to external world.
  • RUN: Used to execute commands within a new layer while building the Docker image
  • CMD: Defines the default command to be executed when a container is launched from the image which can be overridden by user.
  • ENTRYPOINT: Same as CMD but can not be overridden by user.

In the next part, we will be seeing a example of actual Docker Image.

Build Image from Dockerfile

Now open the terminal and go the same directory where the Dockerfile is situated and execute the following command

docker build -t <image_name> .
  • "-t" specifies the tag for the image.
  • The "." at the end specifies same directory which can be changed depending on where the Dockerfile is locacted.

Dockerize a Node.js appllication

Node.js is a JavaScript runtime that is used to write server-side JavaScript applications.

Lets write a Dcokerfile for the node.js apllication

FROM ubuntu:latest

USER root

WORKDIR /app

RUN apt -y update && apt install -y curl && curl -sL https://deb.nodesource.com/setup_22.x | bash -

RUN apt install -y nodejs

COPY . .

RUN npm install

ENTRYPOINT ["npm", "run", "start"]

We are using ubuntu as base image and installing nodejs and coping files from host device to the container.

Use the following command to get the size of the image

docker images

Output:

REPOSITORY                                 TAG                  IMAGE ID       CREATED          SIZE
gfg-node-image-optimation latest e15df68a065d 2 days ago 471MB

Use docker build command to build the image. In the following steps we will optimize this image.

Efficient Dockerfile Strategies for Node.js

Image optimization means reducing size and making it more stable in production environment.

1. Choosing the Right Base Image

We are using a Node.js to create a http server. But the base Ubuntu image does not comes with Node.js pre-installed. So, we have to first install Node.js and npm and manage the version ourselves. This creates extra hassle and takes more space. Better approach would be directly using the Node.js official image as we do not have to do anything our self. Now, lets modify the Dockerfile. Always choose the official or verified from docker hub.

Official Node docker image


Most commonly used tag for node is nodejs image is latest which is alias of current-bullseye. But there are few problem with using it.

  1. It can give inconsistent result as it always searches for the latest version of the image.
  2. It uses a full fledged Debion linuz, so size is generally larger which is not intended.

Solution to these problems are quite simple.

  1. Always specify the version of image
  2. Use lightweight official images like alpine, bookworm-slim, bullseye-slim.
FROM node:22.3.0-bullseye-slim

WORKDIR /app

COPY . .

RUN npm install

ENTRYPOINT ["npm", "run", "start"]

The size of the current image is 348 MB which is already less than the initial image.

2. Leveraging .dockerignore to Exclude Unnecessary Files

This file contains all the files and images you want to exclude from image. This increases the performance of docker.

node_modules
.env

3. Using npm ci Instead of npm install

The npm install command installs all the dependencies including devDependencies which are not needed for production environment. So, we can use npm ci instead. It strictly works on the versions locked in package-lock.json, npm ci ensures that builds are reproducible across different environments. This predictability is crucial for maintaining consistency in production deployments.

FROM node:22.3.0-bullseye-slim

WORKDIR /app

COPY . .

RUN npm ci --only=production

ENTRYPOINT ["npm", "run", "start"]

4. Use multi-stage Builds for Smaller Images

Idea is to use multiple stages to build the image and copying only the necessary images from the previous stage to the new stage. This significantly reduces the size of the image and avoids leaking sensitive information.

FROM node:22.3.0-bullseye-slim AS builder
WORKDIR /app
COPY . .
RUN npm ci --only=production
RUN npm npm i -g @vercel/ncc
RUN ncc build index.js -o dist

FROM node:22.3.0-bullseye-slim
WORKDIR /app
COPY --from=builder /app/dist/index.js .

ENTRYPOINT ["node", "index.js"]

5. Minimizing Layers to Reduce Image Size

Each RUN, COPY, and ADD command creates a new layer. Combine commands to reduce layers. This can be done using running everything in one command. If you're using multistage docker build, then no need to do it.

FROM node:22.3.0-bullseye-slim AS builder

WORKDIR /app

COPY . .

RUN npm ci --only=production && npm npm i -g @vercel/ncc && ncc build index.js -o dist

ENTRYPOINT ["node", "index.js"]

Size comparison of different images

Size comparison

Here is the size comparison for the the images. The oldest the one is the oldest image was created using the first Dockerfile code we have written and the latest one uses multi-staged docker builds.

Conclusion

In this article, we have learned about docker images, procedure of creating docker images from Dockerfile and five techniques for optimizing the docker image for Node.j application.


Next Article
Article Tags :

Similar Reads