IM

ISSA MDOE

Full Stack Software Engineer

Loading0%
2025-12-30 10 min read

Optimizing Docker Builds for Node.js Production

#DevOps#Docker#Node.js
👨🏽‍💻
Issa Ally Mdoe
Full Stack Engineer

A naive Dockerfile for a Node.js app can easily result in images over 1GB, slow deployments, and security vulnerabilities. Here is how I optimize Docker images to be under 100MB and secure.

1. Use Lightweight Base Images

Stop using FROM node:20. It contains a full Debian OS. Use FROM node:20-alpine. It's only ~50MB.

2. Multi-Stage Builds

We don't need Typescript source files or devDependencies in production.

dockerfile
# Stage 1: Build
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
# Install ALL deps for building
RUN npm ci 
COPY . .
RUN npm run build

# Stage 2: Runner FROM node:20-alpine AS runner WORKDIR /app ENV NODE_ENV production COPY package*.json ./ # Install ONLY prod deps RUN npm ci --only=production # Copy built assets from builder COPY --from=builder /app/dist ./dist

CMD ["node", "dist/main.js"] `

3. Layer Caching

Order matters! Docker caches layers.

dockerfile
# BAD
COPY . .
RUN npm install

# GOOD COPY package.json package-lock.json ./ RUN npm install COPY . . `

In the GOOD example, if you change a source file but not dependencies, Docker skips npm install and uses the cache. This speeds up builds by 10x.

4. Security Practices

  • Don't run as root: By default, Docker runs as root. This is dangerous.
  • Use Tini/Dumb-init: Node.js doesn't handle PID 1 signals (like SIGTERM/SIGINT) well. Use an init system to handle graceful shutdowns.

5. .dockerignore

Don't copy unnecessary files.

text
node_modules
dist
.git
.env
Dockerfile
README.md

By applying these patterns, we reduced our image size from 950MB to 78MB and build times from 4 mins to 45 seconds.


Enjoyed this article?
Share it with your network