π³ 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)