Skip to content

Docker_best_practices

This chapter covers best practices for building secure, efficient, and maintainable Docker images and containers.

# Bad - Using latest
FROM node
# Good - Using specific version
FROM node:18-alpine
# Better - Pin to exact version
FROM node:18.17.0-alpine3.18
┌─────────────────────────────────────────────────────────────────────────────┐
│ Image Size Comparison │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Image Size Reduction │
│ ───────────────────────────────────────────────────────────────────── │
│ ubuntu:22.04 ~77 MB (baseline) │
│ ubuntu:22.04-slim ~29 MB 62% smaller │
│ alpine:3.18 ~7 MB 91% smaller │
│ │
│ node:18 ~1 GB │
│ node:18-alpine ~180 MB 82% smaller │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
# Build stage
FROM node:18 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Production stage
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
USER node
CMD ["node", "dist/index.js"]
# Bad - Cache invalidated on every change
COPY . .
RUN npm install
# Good - Cache-friendly order
COPY package*.json ./
RUN npm install
COPY . .
.dockerignore
# Git
.git
.gitignore
# IDE
.vscode
.idea
*.swp
# Dependencies
node_modules
npm-debug.log
# Build artifacts
dist
build
# Tests
coverage
*.test.js
# Documentation
*.md
docs/
# Environment
.env
.env.local
# Create non-root user
RUN addgroup -g 1000 -S appgroup && \
adduser -u 1000 -S appuser -G appgroup
# Set ownership
COPY --chown=appuser:appgroup . .
# Switch to user
USER appuser
# For web applications
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1
# For databases
HEALTHCHECK --interval=10s --timeout=5s --retries=5 \
CMD pg_isready -U postgres || exit 1
Terminal window
# Use Docker Scout
docker scout cves myimage:latest
# Use Trivy
trivy image myimage:latest
# Use Clair
docker scan myimage:latest
# Build stage (bad - secrets in image)
FROM node
ENV API_KEY=secret123
# Better - Use runtime secrets
# Don't include secrets in the image
# Pass secrets at runtime via:
# - Docker secrets (Swarm)
# - Kubernetes secrets
# - Environment variables (external)
Terminal window
# Enable content trust
export DOCKER_CONTENT_TRUST=1
# Pull signed images only
docker pull nginx
# Bad - Multiple RUN layers
RUN apt-get update
RUN apt-get install -y nginx
RUN apt-get clean
RUN rm -rf /var/lib/apt/lists/*
# Good - Single RUN layer
RUN apt-get update && \
apt-get install -y nginx && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
# Bad
RUN wget https://example.com/file1.tar
RUN tar xf file1.tar
RUN rm file1.tar
# Good
RUN wget -q https://example.com/file1.tar && \
tar xf file1.tar && \
rm file1.tar
ARG VERSION=latest
ARG BUILD_DATE
# Use in build
LABEL version="${VERSION}"
LABEL build-date="${BUILD_DATE}"
Terminal window
docker run --read-only myapp
Terminal window
# Drop all capabilities, add only what's needed
docker run --cap-drop all --cap-add NET_BIND_SERVICE myapp
Terminal window
# Store sensitive data in memory
docker run --tmpfs /app/secrets:rw,size=10m myapp
Terminal window
docker run \
--memory 512m \
--memory-reservation 256m \
--cpus 1.0 \
--restart unless-stopped \
myapp
docker-compose.dev.yml
services:
app:
build: .
volumes:
- .:/app
- /app/node_modules
environment:
- NODE_ENV=development
ports:
- "3000:3000"
docker-compose.prod.yml
services:
app:
build: .
environment:
- NODE_ENV=production
restart: unless-stopped
deploy:
resources:
limits:
memory: 512M
cpus: '0.5'
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
Terminal window
docker run \
--log-driver json-file \
--log-opt max-size=10m \
--log-opt max-file=3 \
myapp
Terminal window
# Real-time stats
docker stats
# All containers with no streaming
docker stats --no-stream
┌─────────────────────────────────────────────────────────────────────────────┐
│ Docker Best Practices Checklist │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Images │
│ □ Use specific version tags │
│ □ Use minimal base images (Alpine) │
│ □ Use multi-stage builds │
│ □ Optimize layer caching │
│ □ Add .dockerignore file │
│ □ Scan for vulnerabilities │
│ │
│ Security │
│ □ Don't run as root │
│ □ Add health checks │
│ □ Use read-only filesystem when possible │
│ □ Limit container capabilities │
│ □ Enable Docker Content Trust │
│ │
│ Operations │
│ □ Set resource limits │
│ □ Configure appropriate restart policy │
│ □ Use proper logging configuration │
│ □ Monitor container health │
│ │
│ Development/Production │
│ □ Separate configurations │
│ □ Use Docker Compose for orchestration │
│ □ Implement proper secrets management │
│ │
└─────────────────────────────────────────────────────────────────────────────┘

In this chapter, you learned:

  • Image optimization best practices
  • Security best practices
  • Container best practices
  • Development vs production considerations
  • Monitoring and logging

You’ve completed the Docker Fundamentals section. Now let’s move to Kubernetes.