Performance Issues: Making Docker Fast Again
Docker can be slow. Builds take forever. Containers are sluggish. That's performance issues. But they're fixable.
🎯 The Big Picture​
Think of performance issues like a slow car. It works. But it's slow. You tune it. It gets faster. That's performance troubleshooting.
Performance issues make Docker slow. But they're fixable. Profile. Optimize. Measure. That's how you make it fast.
Common Performance Issues​
Issue 1: Slow Builds​
Problem:
- Builds take 10+ minutes
- Rebuilds are slow
- No caching
Solution:
# Use BuildKit
# syntax=docker/dockerfile:1
# Cache dependencies
RUN --mount=type=cache,target=/root/.npm \
npm ci
# Optimize layer order
COPY package*.json ./
RUN npm install
COPY . .
Why: BuildKit is faster. Caching helps.
Issue 2: Large Images​
Problem:
- Images are 1GB+
- Slow to pull
- Slow to push
Solution:
# Use multi-stage builds
FROM node:18 AS builder
# ... build
FROM node:18-alpine
COPY --from=builder /app/dist ./dist
# Remove unnecessary files
RUN rm -rf /tmp/* /var/tmp/*
Why: Smaller images. Faster transfers.
Issue 3: Slow Container Startup​
Problem:
- Containers take 30+ seconds to start
- Application slow to respond
Solution:
# Use minimal base image
FROM alpine:latest
# Pre-compile if possible
# Use health checks
HEALTHCHECK --interval=30s CMD curl -f http://localhost/health
Why: Minimal images start faster.
Issue 4: High Resource Usage​
Problem:
- Containers use too much CPU
- Containers use too much memory
- Host runs out of resources
Solution:
deploy:
resources:
limits:
cpus: '1.0'
memory: 512M
reservations:
cpus: '0.5'
memory: 256M
Why: Limits prevent resource exhaustion.
The Slow Car Analogy​
Think of performance issues like a slow car:
Slow builds: Engine needs tuning Large images: Car is too heavy Slow startup: Takes time to start High resource usage: Uses too much fuel
Once you see it this way, performance makes perfect sense.
Profiling Performance​
1. Profile Builds​
Measure build time:
# Time the build
time docker build -t my-app .
# Verbose output
docker build --progress=plain -t my-app .
# See where time is spent
Why: Know what's slow.
2. Profile Images​
Measure image size:
# Check image size
docker images
# Detailed size
docker system df -v
# Layer sizes
docker history my-app
Why: Know what's large.
3. Profile Containers​
Measure container performance:
# Monitor resources
docker stats
# Check startup time
time docker run my-app
# Profile application
docker exec container-name top
Why: Know what's slow.
Real-World Example: Performance Optimization​
Before (slow):
FROM ubuntu:20.04
RUN apt-get update && apt-get install -y nodejs npm
COPY . .
RUN npm install
CMD ["node", "server.js"]
Build time: 5 minutes Image size: 800MB Startup time: 10 seconds
After (fast):
# syntax=docker/dockerfile:1
FROM node:18-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN --mount=type=cache,target=/root/.npm \
npm ci --only=production
FROM node:18-alpine
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
USER nodejs
CMD ["node", "server.js"]
Build time: 1.5 minutes Image size: 150MB Startup time: 2 seconds
That's optimization. Faster. Smaller. Better.
Optimization Strategies​
1. Use BuildKit​
Enable BuildKit:
export DOCKER_BUILDKIT=1
docker build -t my-app .
Why: Faster builds. Parallel execution.
2. Use Cache Mounts​
Cache dependencies:
RUN --mount=type=cache,target=/root/.npm \
npm ci
Why: Faster dependency installation.
3. Optimize Layer Order​
Order matters:
# Changes rarely
COPY package*.json ./
RUN npm install
# Changes often
COPY . .
Why: Better caching. Faster rebuilds.
4. Use Multi-Stage Builds​
Smaller images:
FROM node:18 AS builder
# ... build
FROM node:18-alpine
COPY --from=builder /app/dist ./dist
Why: Smaller images. Faster pulls.
5. Use Minimal Base Images​
Smaller base:
# BAD
FROM ubuntu:20.04
# GOOD
FROM alpine:latest
Why: Smaller images. Faster.
Best Practices​
1. Profile First​
Measure before optimizing:
time docker build -t my-app .
docker images my-app
Why: Know what to optimize.
2. Use .dockerignore​
Exclude unnecessary files:
node_modules
.git
.env
*.log
Why: Smaller context. Faster builds.
3. Set Resource Limits​
Prevent exhaustion:
resources:
limits:
cpus: '1.0'
memory: 512M
Why: Prevent resource issues.
4. Monitor Performance​
Track metrics:
- Build time
- Image size
- Startup time
- Resource usage
Why: Know if optimizations work.
5. Clean Up Regularly​
Remove unused resources:
docker system prune -a
Why: Free up space. Better performance.
My Take: Performance Strategy​
Here's what I do:
Build optimization:
- Always use BuildKit
- Cache mounts
- Optimize layer order
- Multi-stage builds
Runtime optimization:
- Minimal base images
- Resource limits
- Health checks
- Regular cleanup
The key: Profile first. Optimize systematically. Measure results.
Memory Tip: The Slow Car Analogy​
Performance issues = Slow car
Slow builds: Engine tuning Large images: Weight reduction Slow startup: Quick start High usage: Fuel efficiency
Once you see it this way, performance makes perfect sense.
Common Mistakes​
- Not profiling: Don't know what's slow
- Wrong base images: Large, slow
- No caching: Slow builds
- Too many layers: Large images
- No resource limits: Resource exhaustion
Hands-On Exercise: Optimize Performance​
1. Profile current build:
time docker build -t my-app .
docker images my-app
# Note build time and size
2. Enable BuildKit:
export DOCKER_BUILDKIT=1
time docker build -t my-app .
# Compare time
3. Add cache mounts:
RUN --mount=type=cache,target=/root/.npm \
npm ci
4. Use multi-stage:
FROM node:18 AS builder
# ... build
FROM node:18-alpine
COPY --from=builder /app/dist ./dist
5. Measure improvement:
time docker build -t my-app .
docker images my-app
# Compare with before
Key Takeaways​
- Profile first - Know what's slow
- Use BuildKit - Faster builds
- Use cache mounts - Faster dependencies
- Optimize layer order - Better caching
- Use multi-stage builds - Smaller images
- Set resource limits - Prevent exhaustion
- Monitor performance - Track improvements
What's Next?​
Now that you understand performance issues, let's learn about production scenarios. Next: Production Scenarios.
Remember: Performance issues are like a slow car. Profile first. Optimize systematically. Measure results. Faster builds. Faster runs. Better performance.