Production engineering guide for container image optimization covering patterns, implementation strategies, and operational best practices.
Container image optimization reduces image size, improves build times, and enhances security by eliminating unnecessary files, layers, and dependencies. This matters when deploying containers to resource-constrained environments, when minimizing network transfer time, or when enforcing security policies that require minimal attack surface.
Remove development dependencies from production images using package manager flags or manual cleanup:
# For npm-based applications, use --production flag during install
npm ci --production
# For Python applications, remove .pyc files and __pycache__ directories
find . -name "*.pyc" -delete
find . -name "__pycache__" -type d -exec rm -rf {} +
# For Ruby applications, clean gems after bundle install
bundle install --without development test
Use multi-stage builds to separate build-time dependencies from runtime requirements:
# Build stage
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --production
COPY . .
RUN npm run build
# Runtime stage
FROM node:18-alpine AS runtime
WORKDIR /app
# Copy only necessary files from builder stage
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
EXPOSE 3000
CMD ["node", "dist/server.js"]
Select minimal base images to reduce total image size:
FROM alpine:latest
# vs. ubuntu:20.04 or centos:7
RUN apk add --no-cache python3 py3-pip
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
Reorder Dockerfile instructions to maximize layer cache reuse:
# Good order: dependencies first, then code
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --production
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]
# Bad order: code changes before dependency installation
FROM node:18-alpine
WORKDIR /app
COPY . .
COPY package*.json ./
RUN npm ci --production
EXPOSE 3000
CMD ["node", "server.js"]
Clean up temporary files and build artifacts after compilation:
# Remove build directories
rm -rf build/
rm -rf node_modules/.cache/
# Remove package manager cache
rm -rf ~/.npm/_cacache/
rm -rf /root/.npm/_cacache/
Use buildx for parallel builds with multiple platforms:
docker buildx build \
--platform linux/amd64,linux/arm64 \
--tag myapp:latest \
--push \
.
Configure build cache to avoid rebuilding unchanged layers:
FROM node:18-alpine
WORKDIR /app
# Copy package files first for better caching
COPY package*.json ./
RUN npm ci --production
# Copy source code after dependencies
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]
Limit build context to only necessary files:
# Use .dockerignore to exclude unnecessary files
echo "node_modules" > .dockerignore
echo "*.log" >> .dockerignore
echo "build/" >> .dockerignore
# Then build with specific context
docker build -f Dockerfile -t myapp:latest .
Specify target platforms to optimize for runtime:
# Build for multiple architectures
docker buildx build \
--platform linux/amd64,linux/arm64 \
--tag myapp:latest \
.
Eliminate unnecessary tools from production images:
FROM alpine:latest
# Remove shell and utilities that are not needed in runtime
RUN apk del --no-cache bash sh
Mount filesystems as read-only to prevent tampering:
# In Kubernetes deployment
apiVersion: v1
kind: Pod
spec:
containers:
- name: app
image: myapp:latest
volumeMounts:
- name: config
mountPath: /etc/config
readOnly: true
securityContext:
readOnlyRootFilesystem: true
Run containers as non-root users:
FROM node:18-alpine
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001
USER nextjs
WORKDIR /app
COPY package*.json ./
RUN npm ci --production
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]
Examine image size breakdown by layer:
# Show image layers
docker history myapp:latest
# Analyze image size
docker inspect myapp:latest | jq '.[0].Size'
Scan images for security issues:
# Using trivy or clair
trivy image myapp:latest
# Check for base image vulnerabilities
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock aquasec/trivy:latest image myapp:latest
Ensure optimized images behave correctly:
# Run with specific resource limits
docker run --memory=128m --cpus=0.5 myapp:latest
# Test startup time and memory usage
docker run -d --name test-app myapp:latest
docker stats --no-stream test-app
Changing file order in Dockerfile breaks cache reuse:
# This invalidates cache on every code change
FROM node:18-alpine
WORKDIR /app
COPY . .
COPY package*.json ./
RUN npm ci --production
Files not excluded from build context increase image size:
# Without proper .dockerignore, these may be included:
.git/
.gitignore
node_modules/
*.log
*.tmp
build/
coverage/
Removing necessary files during optimization:
# This removes config files needed at runtime
RUN rm -rf /app/config
# Should instead copy required files:
COPY --chown=1001:1001 ./config ./config
Mixing build and runtime environments incorrectly:
# Incorrect: copying development tools to runtime
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Runtime stage copies unnecessary build tools
FROM node:18-alpine AS runtime
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
# Missing: remove build dependencies from runtime
Running optimized containers without proper resource limits:
# Without memory limits, optimized images may still consume too much
docker run -m 128m myapp:latest
# May crash due to memory spikes during processing
Changing layer order after optimization causes rebuilds:
# Changing from this:
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --production
COPY . .
# To this breaks cache:
FROM node:18-alpine
WORKDIR /app
COPY . .
COPY package*.json ./
RUN npm ci --production
Optimizing for one environment but running in another:
# Production image optimized for ARM64, but deployed on AMD64
docker buildx build --platform linux/amd64 -t myapp:latest .
# May cause performance issues or runtime errors
Optimized images without security context:
# Kubernetes deployment missing security context
apiVersion: v1
kind: Pod
spec:
containers:
- name: app
image: myapp:latest
# Missing readOnlyRootFilesystem and user settings
Use compressed base images where available:
FROM alpine:latest
# vs. ubuntu:20.04 which is typically larger
RUN apk add --no-cache python3 py3-pip
Distribute applications as static binaries:
# For Go applications
FROM golang:1.19-alpine AS builder
WORKDIR /app
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main .
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /app/main .
CMD ["./main"]
Optimize for specific hardware characteristics:
# ARM64 optimized image
FROM --platform=linux/arm64 node:18-alpine
# Platform-specific optimizations
RUN npm config set platform linux-arm64
Add health checks to optimize container lifecycle management:
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --production
COPY . .
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1
EXPOSE 3000
CMD ["node", "server.js"]
Monitor image size changes over time:
# Compare image sizes
docker images myapp
# Track with CI pipeline
docker build -t myapp:${BUILD_NUMBER} .
docker tag myapp:${BUILD_NUMBER} myapp:latest
Measure runtime performance after optimization:
# Monitor startup time
time docker run myapp:latest
# Monitor resource usage
docker stats --no-stream myapp-container
Regularly audit optimized images for vulnerabilities:
# Run security scan after each build
trivy image myapp:${BUILD_NUMBER}
# Check for base image updates
docker pull node:18-alpine
The techniques described here apply to any containerization platform that supports Dockerfile syntax and standard OCI image formats. They focus on concrete actions and measurable outcomes rather than vendor-specific features or configurations.
This page was rewritten on 15 October 2026. It replaced a templated version that was not specific to its own title. How these pages are written and checked. If anything here is wrong, tell us at [email protected] and we will correct it.
We use cookies for analytics (Google Analytics) and advertising (Google AdSense) to improve your experience and support free content. Privacy Policy