Gajanand Sharma
Writing
dockernodejsoptimization

šŸš€ Optimizing Docker for Node.js: From 1.34 GB to 267 MB! 🐳✨

In this blog, we’ll walk you through our before and after Dockerfiles, highlight the optimizations, and share tips for creating…

Ā·4 min readĀ·

šŸš€ Optimizing Docker for Node.js: From 1.34 GB to 267 MB! 🐳✨

In this blog, we’ll walk you through our before and after Dockerfiles, highlight the optimizations, and share tips for creating production-ready Docker images.


Introduction

Building Docker images for production is a balancing act between functionality and efficiency. A bloated image not only wastes storage but also slows down deployments, builds, and scaling. Recently, we embarked on a journey to optimize our Node.js application’s Docker image and managed to shrink it from 1.34 GB to just 267 MBā€Šā€”ā€Ša whopping 80% reduction!


The Problem: A 1.34 GB Monster 🐘

Here’s where we startedā€Šā€”ā€Ša typical Dockerfile for a Node.js application:

**Dockerfile.un-optimized**

bash
# Use the Node.js image
FROM node:lts
 
# Set the working directory in the container
WORKDIR /usr/src/app
 
# Copy package.json and package-lock.json
COPY package*.json ./
 
# Install dependencies
RUN npm install
 
# Copy the rest of the application code
COPY . .
 
# Build the application
RUN npm run build
 
# Expose the application port
EXPOSE 3000
 
# Define the command to run the application
CMD ["node", "dist/main"]

This Dockerfile does its jobā€Šā€”ā€Šbuilds and runs the applicationā€Šā€”ā€Šbut it’s far from efficient. Why?

The result? A 1.34 GB image that’s slow to build and deploy.


The Solution: A 267 MB Work of ArtĀ šŸ–Œļø

To address these inefficiencies, we reimagined the Dockerfile using multi-stage builds and the lightweight **node:lts-alpine** base image.

**Dockerfile.optimized**

javascript
# Stage 1: Build Stage
FROM node:lts-alpine AS build
 
# Set the working directory
WORKDIR /usr/src/app
 
# Copy package.json and package-lock.json
COPY package*.json ./
 
# Install all dependencies on given versions
RUN npm ci
 
# Copy the application code
COPY . .
 
# Build the application
RUN npm run build
 
# Stage 2: Runtime Stage
FROM node:lts-alpine
 
# Set the working directory
WORKDIR /usr/src/app
 
# Copy only the built files and production dependencies
COPY --from=build /usr/src/app/dist ./dist
COPY --from=build /usr/src/app/package*.json ./
 
# Expose the application port
EXPOSE 3000
 
# Start the application
CMD ["node", "dist/main"]

Key Optimizations

Let’s break down the magic:

1. Multi-Stage Builds

2. Lightweight BaseĀ Image

3. Production Dependencies Only


The Results


Benefits BeyondĀ Size

  1. Faster CI/CD Pipelines:

2. Improved Security:

3. Cost Savings:

4. Ease of Debugging:


Final Thoughts

Optimizing your Docker images is an investment in performance, security, and maintainability. By adopting best practices like multi-stage builds and lightweight base images, we not only improved our workflows but also set the stage for smoother scaling and faster deployments.

Have you optimized a Docker image before? Share your tips and tricks in the comments! Let’s keep the conversation going. šŸš€šŸ³


What do you think of this journey? Let me know if you’d like more examples or technical deep dives! šŸš€

Written by Gajanand Sharma

Building AI-powered backend systems, automating business operations, and shipping products.