Dockerizing Microservices: Minimizing Image Size & Image Safety

The Danger of Bloated Containers

Building Docker containers without optimization results in large images containing compiler tooling, test files, and development packages. Bloated images take longer to transfer over networks and increase the attack surface of your production systems.

Case Study: Slow Kubernetes Scaling

A web platform suffered scaling failures during peak traffic because Kubernetes could not start new pods quickly enough. The culprit was the container image size (1.4GB), which took 90-120 seconds to download onto new worker nodes.

The Bug: Bloated Single-Stage Dockerfile

The original Dockerfile compiled a Node.js app in a single stage, retaining dev dependencies:

FROM node:18
WORKDIR /app
COPY . .
RUN npm install
RUN npm run build
EXPOSE 3000
CMD ["npm", "start"]

The Fix: Multi-stage Builds & Non-Root Execution

We refactored the Dockerfile to use a multi-stage compilation pipeline, copying only production assets to a minimal base image, and configured it to run as a non-privileged user:

# Stage 1: Build
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# Stage 2: Runtime
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY --from=builder /app/dist ./dist

# Configure Security
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser

EXPOSE 3000
CMD ["node", "dist/index.js"]

This simple refactoring reduced the final Docker image size from 1.4GB to 160MB, allowing Kubernetes to pull images and spin up new pods in under 4 seconds.

Scroll to Top