Docker Image Optimization- How I Cut Container Size By 70%

Docker Image Optimization- How I Cut Container Size By 70%
Author: Pranay ShastriPublished on December 19, 2025 at 02:00 PM

Post Synopsis

Learn how to reduce Docker image size by 70% using simple Docker image optimization techniques. Make containers smaller, faster, and more secure.

Large Docker images are a common challenge in containerized applications, leading to slower deployments, increased bandwidth costs, and potential security vulnerabilities. When container images exceed reasonable sizes, they can significantly impact your development workflow and production performance. In this detailed post, we’ll learn Docker image optimization techniques to minimize Docker image size while maintaining application functionality and security.

Why Docker Image Size Matters?

Before learning about the solutions, let me explain why this even matters:

  • 1. Faster deployments – Smaller images upload/download quicker
  • 2. Less bandwidth costs – Save money on data transfer
  • 3. Better security – Fewer packages = smaller attack surface
  • 4. Faster startups – Containers boot up quicker
  • 5. Less disk space – More apps on the same server

Think of it like packing for a trip. You can either bring your whole closet (huge container) or just what you need (small container). Which plane departs faster?

My Original Fat Dockerfile

Here’s an example of a problematic Dockerfile:

```dockerfile

# My terrible, huge Dockerfile

FROM node:18

WORKDIR /app

# Copy everything (bad idea!)

COPY . .

# Install ALL dependencies

RUN npm install

# Expose port

EXPOSE 3000

# Start app

CMD ["npm", "start"]

```

This gave me a 1.2GB image.

Technique 1: Use Alpine Linux

The biggest win was switching to Alpine Linux. Regular Node.js images are based on Debian/Ubuntu, which are huge. Alpine is like a diet version.

```dockerfile

# Before (big)

FROM node:18

# After (much smaller)

FROM node:18-alpine

```

Just this one change saved me about 300MB.

Technique 2: Multi-Stage Builds

This approach is a game changer. Instead of shipping build tools to production, building in one container and copying only necessary files to another is more efficient.

```dockerfile

# 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

COPY --from=builder /app/package*.json ./

COPY --from=builder /app/node_modules ./node_modules

COPY --from=builder /app/dist ./dist

EXPOSE 3000

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

```

This saved another 200MB by removing build dependencies.

Technique 3: .dockerignore File

Copying unnecessary files into containers increases image size. Logs, test files, git history, and other development artifacts all consume space.

Creating a `.dockerignore` file prevents unnecessary files from being included:

node_modules

npm-debug.log

.git

.gitignore

README.md

.env

.nyc_output

coverage

.DS_Store

.vscode

tests/

*.test.js

This prevents hundreds of MB of unnecessary files from entering the image.

Technique 4: Clean Up Cache Files

Package managers leave cache files behind. Cleaning them up immediately after installing dependencies saves space:

FROM node:18-alpine

WORKDIR /app

COPY package*.json ./

# Install deps and clean cache in one command

RUN npm ci --only=production && npm cache clean --force

COPY . .

EXPOSE 3000

CMD ["npm", "start"]

Saved another 50MB!

Technique 5: Use Specific Versions

Using specific version tags rather than `latest` ensures reproducible builds and can result in smaller images:

# Less specific (might get bigger updates)

FROM node:18-alpine

# More specific (consistent size)

FROM node:18.17.1-alpine

Technique 6: Remove Development Dependencies

Installing development dependencies in production images is a common mistake that adds unnecessary packages.

Wrong way

RUN npm install

# Right way

RUN npm ci --only=production

This removed testing frameworks, linters, and build tools from production.

My Final Optimized Dockerfile

Here’s an example of an optimized final Dockerfile:

# Build stage

FROM node:18.17.1-alpine AS builder

# Install build tools

RUN apk add --no-cache python3 make g++

WORKDIR /app

# Copy package files

COPY package*.json ./

# Install production dependencies and clean cache

RUN npm ci --only=production && npm cache clean --force

# Copy source code

COPY . .

# Build the app

RUN npm run build

# Production stage

FROM node:18.17.1-alpine

WORKDIR /app

# Create non-root user for security

RUN addgroup -g 1001 -S nodejs

RUN adduser -S nextjs -u 1001

# Copy only what we need from builder

COPY --from=builder /app/package*.json ./

COPY --from=builder /app/node_modules ./node_modules

COPY --from=builder /app/dist ./dist

# Change ownership

RUN chown -R nextjs:nodejs /app

USER nextjs

EXPOSE 3000

# Health check

HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \

  CMD curl -f http://localhost:3000/health || exit 1

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

Results

Here’s what can be achieved with these optimization techniques:

Dockerfile VersionImage SizeReduction
Original Fat Version1200MB
With Alpine Linux900MB25% smaller
With Multi-stage Build650MB46% smaller
With All Optimizations350MB71% smaller

That’s a 71% reduction. With optimizations like these, deployments can go from 10 minutes to 3 minutes.

Common Optimization Mistakes

Mistake 1: Copying Everything

Copying the entire project folder before installing dependencies breaks Docker’s layer caching.

Wrong way:

COPY . .

RUN npm install

Right way:

COPY package*.json ./

RUN npm install

COPY . .

Mistake 2: Installing Dev Dependencies in Production

This adds hundreds of MB of unnecessary packages.

Mistake 3: Not Cleaning Package Manager Cache

Leftover cache files take up unnecessary space.

Mistake 4: Using Root User

Security risk and larger image footprint.

Essential Checklist for Smaller Images

Following these practices consistently produces optimal results:

  1. Use Alpine Linux base images
  2. Implement multi-stage builds
  3. Create `.dockerignore` file
  4. Install only production dependencies
  5. Clean package manager cache
  6. Use specific version tags
  7. Run as non-root user
  8. Copy files in the right order for caching

Tools to Measure Image Size

Use these commands to track optimization progress:

```bash

# See all image sizes

docker images

# Check specific image

docker images | grep your-app

# Detailed image analysis

docker history your-app:latest

```

Essential Optimization Tips

1. Start with Official Images

Official images are usually better optimized than community ones.

2. Use DockerSlim (Optional)

For extreme shrinking:

# This can reduce images even more
docker-slim build your-app:latest

3. Monitor Base Image Sizes

Check Docker Hub for image sizes before choosing:

  • node:18-alpine (~120MB)
  • node:18 (~900MB)
  • Huge difference!

4. Combine RUN Commands

Each RUN creates a layer. Combine related commands:

# Creates 2 layers
RUN apt-get update
RUN apt-get install curl

# Creates 1 layer
RUN apt-get update && apt-get install curl

When NOT to Optimize

Sometimes, aggressive optimization isn’t worth it:

  1. Development containers – Build time matters more than size
  2. Small apps – If your image is already < 100MB, don’t over-optimize
  3. Special requirements – Some apps need full Linux distributions

Quick Start Example

Here’s a minimal optimized Dockerfile template:

FROM node:18-alpine

WORKDIR /app

# Copy package files first for better caching
COPY package*.json ./

RUN npm ci --only=production && npm cache clean --force

# Copy app code
COPY . .

# Run as non-root user
RUN addgroup -g 1001 -S nodejs && \
    adduser -S nextjs -u 1001 && \
    chown -R nextjs:nodejs /app

USER nextjs

EXPOSE 3000

CMD ["npm", "start"]

This alone will cut most Node.js app sizes by 50%+.

Summary

Reducing Docker image size isn’t rocket science, but it makes a huge difference:

  • Faster deployments = happier developers
  • Lower costs = happier finance teams
  • Better security = happier security teams
  • Improved performance = happier users

These optimization techniques have been proven in production environments and consistently deliver significant size reductions while maintaining application performance.

Getting Started

To implement these optimizations in your projects:

  1. Audit your current image size with docker images
  2. Select the most impactful technique for your use case
  3. Apply the changes incrementally
  4. Measure the results after each modification

Docker image optimization is an iterative process. Begin with one technique, evaluate its impact, and then proceed to the next optimization.