π³ Docker Image Optimization Guide β The Ultimate Cheat Sheet π
Optimize your Docker images for faster builds, smaller size, better caching, and production readiness. Letβs go!
π§± 1. Use a Small & Specific Base Image π₯
β Do This:
# Use lightweight Alpine variant FROM node:20-alpine
β Avoid This:
# Heavy image β more layers, longer build times FROM ubuntu
π Why?
- Smaller base = smaller image.
- Alpine images are ~5MB vs Ubuntuβs ~100MB.
- Smaller size = faster download, upload, deploy.
πͺ 2. Order Instructions for Layer Caching π
β Do This:
# Caches `npm install` unless package.json changes COPY package*.json ./ RUN npm install # Copy rest of the source after deps are installed COPY . .
β Avoid This:
COPY . . # π invalidates cache if any file changes RUN npm install
π Why?
Docker caches layers. Changing a later step invalidates all subsequent layers. Put stable steps early for faster rebuilds.
π§Ή 3. Remove Unnecessary Files with .dockerignore
π«
β
Example .dockerignore
:
node_modules Dockerfile .dockerignore .git npm-debug.log
π Why?
It prevents Docker from copying unnecessary files into the build context. Less context = faster build and smaller image.
ποΈ 4. Use Multi-Stage Builds π§ͺ
β Example:
# Stage 1: Builder FROM node:20-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm install COPY . . # Stage 2: Runtime FROM node:20-alpine WORKDIR /app COPY --from=builder /app . EXPOSE 8000 CMD ["npm", "start"]
π Why?
- Separate build dependencies from runtime.
- Final image contains only whatβs needed to run the app.
- Reduces image size by up to 70%.
π§Ό 5. Remove Unused Dependencies π§Ή
β
Use --production
for Node.js:
RUN npm ci --only=production
OR
npm prune --production
π Why?
Avoid bundling dev tools and test libraries into your production image.
π¦ 6. Combine RUN Commands π
β Do This:
RUN apk add --no-cache bash curl && \ rm -rf /var/cache/apk/*
β Donβt Do:
RUN apk add bash RUN apk add curl
π Why?
Each RUN
creates a layer. Combining reduces total layers = smaller image size.
π 7. Use --no-cache
for Package Managers π₯
β Alpine:
RUN apk add --no-cache curl
β APT (Debian/Ubuntu):
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
π Why?
Avoids unnecessary cache files and reduces image size.
π 8. Avoid Root User (Security Best Practice) π
# Create a non-root user RUN addgroup -S appgroup && adduser -S appuser -G appgroup USER appuser
π Why?
Running as root inside containers is risky. Use non-root users for security.
πͺ 9. Clean Up Temporary Files π§Ό
RUN npm install && \ npm cache clean --force
π Why?
Removes package cache after install to reduce bloat.
πͺ 10. Use Specific Tags (Not latest
) π―
FROM node:20.11.1-alpine
π Why?
Using latest
can break builds if the base image updates and introduces changes. Always pin versions for reliability.
π 11. Scan for Vulnerabilities π§¬
docker scan my-node-app
Or use:
- Docker Scout
- Trivy (Aqua Security)
- Snyk
π§ͺ 12. Analyze Image Size and Layers π
docker image inspect my-node-app docker history my-node-app
Or use tools like:
- Dive:
dive my-node-app
- DockerSlim:
docker-slim build my-node-app
π§ Final Pro Tips π‘
Tip | Benefit |
---|---|
Use npm ci instead of npm install | Faster and more reliable |
Group COPY steps wisely | Better cache usage |
Avoid adding .env or secrets | Security risk |
Label images (LABEL maintainer=... ) | Better documentation |
Run production builds with NODE_ENV=production | Removes dev dependencies |
π Sample Optimized Dockerfile for Node.js App
# π· Stage 1: Build FROM node:20-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . # β¨ Stage 2: Runtime FROM node:20-alpine WORKDIR /app COPY --from=builder /app . ENV NODE_ENV=production RUN npm prune --production EXPOSE 8000 CMD ["npm", "start"]
Top comments (0)