Skip to main content
A real walkthrough of shrinking bloated Docker images from 1.2GB to 240MB using multi-stage builds, Alpine, and dependency auditing.

How We Cut Our Docker Image Size by 80% and Why It Matters

KU
Kiril Urbonas
6 months ago • 2 min read•15 views

A real walkthrough of shrinking bloated Docker images from 1.2GB to 240MB using multi-stage builds, Alpine, and dependency auditing.

Key takeaways

A real walkthrough of shrinking bloated Docker images from 1.2GB to 240MB using multi-stage builds, Alpine, and dependency auditing.

How We Cut Our Docker Image Size by 80% and Why It Matters#

Our main API image had grown to 1.2GB. Deploys were slow, registry costs were climbing, and cold starts on Kubernetes took 45 seconds. Here's exactly how we fixed it.

The Problem#

A quick docker history revealed the culprits:

  • Full Node.js 18 base image (900MB layer)
  • Dev dependencies included in the final image
  • Leftover build artifacts and cache directories

Step 1: Multi-Stage Builds#

We split the Dockerfile into build and runtime stages:

dockerfile.dockerfile
# Build stage
FROM node:18 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# Runtime stage
FROM node:18-alpine AS runtime
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY package*.json ./
EXPOSE 3000
CMD ["node", "dist/index.js"]

This alone dropped us from 1.2GB to 480MB.

Step 2: Prune Dev Dependencies#

We added a production-only install in the runtime stage:

dockerfile.dockerfile
FROM node:18-alpine AS runtime
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY package*.json ./
RUN npm ci --omit=dev && npm cache clean --force

Down to 310MB.

Step 3: Audit What's Left#

Running npx depcheck found 12 unused packages. We removed them and dropped another 70MB.

Results#

MetricBeforeAfter
Image size1.2GB240MB
Pull time38s8s
Cold start45s12s
Registry cost$180/mo$40/mo

Best Practices#

  1. Always use multi-stage builds for compiled or bundled applications
  2. Use Alpine or distroless base images in production
  3. Run npm ci --omit=dev to exclude dev dependencies
  4. Add a .dockerignore to prevent copying node_modules, .git, and test files
  5. Audit images quarterly with docker scout or trivy

Small images mean faster deploys, cheaper storage, and a smaller attack surface. There's no reason to ship your test framework to production.

React

Get the DevOps Troubleshooting Cheat Sheet

Subscribe and get our free one-page reference for the errors that eat an afternoon — CrashLoopBackOff, OOMKilled, Terraform state locks, and more — plus new guides as we publish them.

Share this post
KU

About Kiril Urbonas

DevOps Engineer

550 articles
View all articles by Kiril Urbonas

You might have missed

Evergreen posts worth revisiting.