DEV Community

Sachin Kasana
Sachin Kasana

Posted on • Originally published at sachinkasana.Medium on

Dockerizing Node.js for Developers (Not DevOps)

“Docker doesn’t have to be scary — especially when it’s built for developers, not DevOps pros.”

If you’ve ever tried deploying a Node.js app and ended up tangled in a mess of configs, bloated images, or painful restarts — this one’s for you. In this guide, I’ll show you how to Dockerize your Node.js app the right way:

✅ Clean, production-ready images

✅ Seamless local dev with hot-reloading

✅ Zero-downtime restarts using PM2

✅ No DevOps overkill

🚀 Why Docker?

Let’s break the usual cycle:

  • “Works on my machine” issues 🧯
  • Environment drift 😵‍💫
  • Downtime every time you push code 😫

With Docker:

  • You define exactly how your app runs.
  • You get reproducible builds.
  • You can deploy with zero downtime using tools like PM2.

🧱 Project Structure

my-app/
├── Dockerfile
├── ecosystem.config.js
├── docker-compose.yml
├── package.json
├── tsconfig.json
├── src/
│ └── server.ts
└── dist/ # for production build
Enter fullscreen mode Exit fullscreen mode

We’ll write:

  • A clean multi-stage Dockerfile
  • A PM2 setup for graceful reloads
  • A docker-compose.yml for local dev

📦 Multi-Stage Dockerfile

# ----- Base image (dependencies) -----
FROM node:18-slim AS base
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev

# ----- Build stage (TypeScript) -----
FROM node:18-slim AS builder
WORKDIR /app
COPY . .
RUN npm install
RUN npm run build

# ----- Production image -----
FROM node:18-slim
WORKDIR /app

# Copy deps & dist only
COPY --from=base /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist
COPY ecosystem.config.js .

# Use PM2 in cluster mode
RUN npm install -g pm2
CMD ["pm2-runtime", "ecosystem.config.js"]
Enter fullscreen mode Exit fullscreen mode

✅ Clean separation

✅ Smaller, faster image

✅ Dev dependencies stay out of production

🧠 ecosystem.config.js (PM2 Setup)

module.exports = {
  apps: [{
    name: "my-app",
    script: "./dist/server.js",
    instances: "max",
    exec_mode: "cluster", // zero-downtime restarts
    watch: false
  }]
};
Enter fullscreen mode Exit fullscreen mode

pm2-runtime keeps your app alive with auto-restart & zero downtime.

⚙️ docker-compose.yml (Dev Convenience)

version: "3.8"

services:
  node:
    build:
      context: .
    ports:
      - "3000:3000"
    volumes:
      - .:/app
      - /app/node_modules
    command: npm run dev
Enter fullscreen mode Exit fullscreen mode

Add this to package.json:

"scripts": {
  "dev": "nodemon --watch src --exec ts-node src/server.ts",
  "build": "tsc"
}
Enter fullscreen mode Exit fullscreen mode

Now just run:

docker-compose up
Enter fullscreen mode Exit fullscreen mode

✅ Instant hot-reload dev experience

✅ Consistent across machines

🔥 Zero-Downtime with PM2

Most people run:

pm2 restart my-app
Enter fullscreen mode Exit fullscreen mode

🚨 This causes downtime.

Instead, use:

pm2 reload my-app
Enter fullscreen mode Exit fullscreen mode

✅ It:

  • Spins up new instance
  • Waits until it’s ready
  • Gracefully shuts down old one

🔐 .dockerignore

Add one to keep your build lean:

node_modules
dist
.git
.env
*.log
Enter fullscreen mode Exit fullscreen mode

🧊 Final Image Size?

  • From ~700MB<150MB using multi-stage + slim base + no dev deps
  • PM2 ensures zero downtime and handles scaling across CPUs

📌 TL;DR

Docker file

🙌 About Me

I’m Sachin — Principal Engineer, blog writer, and builder of clean, scalable software.

👉 Explore more of my work & projects on

🌐 sachinkasana-dev.vercel.app

🌐 https://json-formatter-dev.vercel.app/

📂 Direct Gist URL:

👉 https://gist.github.com/sachinkasana/70617b3ef629c7277a34a2f697532dd1

I regularly write about:

  • Real-world architecture patterns
  • Node.js & TypeScript best practices
  • Dev tools, performance, and clean code

Top comments (0)

Tiugo image

Fast, Lean, and Fully Extensible

CKEditor 5 is built for developers who value flexibility and speed. Pick the features that matter, drop the ones that don’t and enjoy a high-performance WYSIWYG that fits into your workflow

Start now