← Back to all stories

My Docker Image Went on a Diet: From 2GB to 47MB

A tale of multi-stage builds, Alpine adventures, and the satisfaction of watching your image size shrink dramatically.

"Why is our deployment taking 15 minutes?" my manager asked. The answer was embarrassingly simple: we were pushing a 2GB Docker image. Here's how I fixed it.

📦 The 2GB Problem

Our original Dockerfile was... let's call it "naive":

Dockerfile (the bad one)
FROM node:18
WORKDIR /app
COPY . .
RUN npm install
RUN npm run build
CMD ["npm", "start"]

Simple, right? But that node:18 base image is already ~900MB. Add node_modules, build artifacts, and suddenly you're pushing 2GB to your registry every deployment.

🏗️ Multi-Stage Builds

The first optimization: separate build and runtime. Why ship your build tools to production?

Dockerfile (multi-stage)
# Build stage
FROM node:18 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# Production stage
FROM node:18-slim
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
CMD ["node", "dist/index.js"]

Result: 1.2GB → 450MB. Better, but we can do more.

🏔️ The Alpine Switch

Alpine Linux images are tiny. The trade-off? They use musl instead of glibc, which can cause issues with some native modules. For our Node.js app, it worked perfectly:

Dockerfile (final version)
# Build stage
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build

# Production stage
FROM node:18-alpine
WORKDIR /app
RUN addgroup -g 1001 -S nodejs && \
    adduser -S nodejs -u 1001
COPY --from=builder --chown=nodejs:nodejs /app/dist ./dist
COPY --from=builder --chown=nodejs:nodejs /app/node_modules ./node_modules
USER nodejs
EXPOSE 3000
CMD ["node", "dist/index.js"]

🎉 The Final Result

📊
Image size comparison:
Before: 2.1GB
After: 47MB
Reduction: 97.8%

Deployment time dropped from 15 minutes to under 2 minutes. Registry costs went down. Developers stopped complaining about slow builds. Everyone wins.

1️⃣

Use Multi-Stage

Don't ship build tools to production

2️⃣

Choose Alpine

When possible, use -alpine variants

3️⃣

Production Only

Use --only=production for npm

4️⃣

Non-Root User

Security bonus: run as non-root


What's your Docker image diet story? Share your before/after!

Share this story