Skip to content
Container Best Practices logo

Container Best Practices

Expert guidance on dockerfile optimization, multi-stage builds, layer caching strategies, and base image selection. Activates when working with "dockerfile", "docker-compose", "multi-stage", "container optimization", "image layers", or "build cache".

SKILL.md

Full skill instructions

Container Best Practices Skill

Overview

Apply industry-standard best practices for building optimized, maintainable, and efficient Docker containers. Master multi-stage builds, layer caching, base image selection, and build optimization strategies to create production-ready container images.

Core Principles

Write Efficient Dockerfiles

Order Instructions for Maximum Cache Efficiency:

Structure Dockerfile layers from least to most frequently changing:

# 1. Base image (changes rarely)
FROM node:20-alpine AS base

# 2. System dependencies (changes occasionally)
RUN apk add --no-cache \
    python3 \
    make \
    g++

# 3. Working directory setup
WORKDIR /​app

# 4. Package manifest files (changes moderately)
COPY package.json package-lock.json ./

# 5. Dependencies installation (leverages cache when manifest unchanged)
RUN npm ci --only=production

# 6. Application code (changes frequently)
COPY . .

# 7. Build step (only when code changes)
RUN npm run build

# 8. Runtime configuration
EXPOSE 3000
CMD ["node", "dist/​main.js"]

Combine RUN Commands to Reduce Layers:

Minimize image size by chaining related commands:

# Bad: Creates 3 layers
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get clean

# Good: Creates 1 layer
RUN apt-get update && \
    apt-get install -y curl && \
    apt-get clean && \
    rm -rf /​var/​lib/​apt/​lists/​*

Use .dockerignore to Exclude Unnecessary Files:

Create .dockerignore to prevent copying build artifacts and sensitive files:

# Development files
node_modules/
npm-debug.log*
.git/
.gitignore

# Test files
*.test.js
coverage/
.nyc_output/

# Documentation
README.md
docs/

# IDE files
.vscode/
.idea/

# Environment files
.env
.env.local
*.key
*.pem

Implement Multi-Stage Builds

Separate Build and Runtime Environments:

Use multi-stage builds to create minimal production images:

# Stage 1: Build environment with all dev dependencies
FROM node:20 AS builder
WORKDIR /​app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build && \
    npm run test

# Stage 2: Production environment with only runtime dependencies
FROM node:20-alpine AS production
WORKDIR /​app

# Copy only necessary files from builder
COPY --from=builder /​app/​dist ./​dist
COPY --from=builder /​app/​node_modules ./​node_modules
COPY package*.json ./

# Security: Run as non-root user
RUN addgroup -g 1001 -S nodejs && \
    adduser -S nodejs -u 1001 && \
    chown -R nodejs:nodejs /​app

USER nodejs
EXPOSE 3000
CMD ["node", "dist/​main.js"]

Create Specialized Build Targets:

Define multiple targets for different use cases:

# Development stage with hot reload
FROM node:20-alpine AS development
WORKDIR /​app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000 9229
CMD ["npm", "run", "dev"]

# Testing stage with test dependencies
FROM development AS testing
RUN npm run lint && \
    npm run test:unit && \
    npm run test:integration

# Build stage
FROM node:20 AS builder
WORKDIR /​app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# Production stage (minimal)
FROM node:20-alpine AS production
WORKDIR /​app
COPY --from=builder /​app/​dist ./​dist
COPY --from=builder /​app/​node_modules ./​node_modules
USER node
EXPOSE 3000
CMD ["node", "dist/​main.js"]

Build specific stages:

# Development
docker build --target development -t myapp:dev .

# Testing
docker build --target testing -t myapp:test .

# Production
docker build --target production -t myapp:prod .

Optimize Base Image Selection

Choose Minimal Base Images:

Select the smallest viable base image for your runtime:

# Python examples by size
FROM python:3.11-alpine      # ~50MB (best for simple apps)
FROM python:3.11-slim        # ~120MB (good compatibility)
FROM python:3.11             # ~900MB (avoid in production)

# Node.js examples by size
FROM node:20-alpine          # ~110MB (best for production)
FROM node:20-slim            # ~170MB (good compatibility)
FROM node:20                 # ~900MB (avoid in production)

# Distroless images (Google)
FROM gcr.io/​distroless/​nodejs20-debian12  # Minimal attack surface
FROM gcr.io/​distroless/​python3-debian12   # No shell, no package manager

Use Distroless for Maximum Security:

Distroless images contain only your application and runtime dependencies:

# Build stage
FROM node:20 AS builder
WORKDIR /​app
COPY package*.json ./
RUN npm ci --only=production
COPY . .

# Production: Distroless image
FROM gcr.io/​distroless/​nodejs20-debian12
WORKDIR /​app
COPY --from=builder /​app/​node_modules ./​node_modules
COPY --from=builder /​app/​dist ./​dist
USER nonroot:nonroot
EXPOSE 3000
CMD ["dist/​main.js"]

Pin Specific Versions:

Always use specific version tags for reproducibility:

# Bad: Version changes unexpectedly
FROM node:20

# Good: Specific version pinned
FROM node:20.11.1-alpine3.19

# Better: Use digest for immutability
FROM node:20.11.1-alpine3.19@sha256:abc123...

Leverage Build Cache Effectively

Structure for Cache Reuse:

Place frequently changing instructions after stable ones:

FROM python:3.11-slim

# System packages (rarely change)
RUN apt-get update && \
    apt-get install -y --no-install-recommends \
    postgresql-client && \
    apt-get clean

# Requirements (change occasionally)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Application code (changes frequently)
COPY . .

CMD ["python", "app.py"]

Use BuildKit Cache Mounts:

Enable BuildKit for advanced caching:

# syntax=docker/​dockerfile:1.4

FROM python:3.11-slim

# Cache pip downloads across builds
RUN --mount=type=cache,target=/​root/​.cache/​pip \
    pip install -r requirements.txt

# Cache apt packages
RUN --mount=type=cache,target=/​var/​cache/​apt \
    --mount=type=cache,target=/​var/​lib/​apt \
    apt-get update && \
    apt-get install -y postgresql-client

Build with BuildKit:

DOCKER_BUILDKIT=1 docker build -t myapp:latest .

Implement Layer Caching in CI/​CD:

Use registry cache for faster CI builds:

# GitHub Actions with BuildKit cache
docker buildx build \
  --cache-from type=registry,ref=ghcr.io/​org/​app:cache \
  --cache-to type=registry,ref=ghcr.io/​org/​app:cache,mode=max \
  --tag ghcr.io/​org/​app:latest \
  --push .

Optimize Runtime Configuration

Configure Health Checks:

Define health checks for container orchestration:

FROM node:20-alpine
WORKDIR /​app
COPY . .

# HTTP health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD node healthcheck.js

# Alternative: Using curl
HEALTHCHECK --interval=30s --timeout=3s \
  CMD curl -f http://localhost:3000/​health || exit 1

EXPOSE 3000
CMD ["node", "server.js"]

Set Appropriate Working Directory:

Always use absolute paths for WORKDIR:

# Bad: Relative path
WORKDIR app

# Good: Absolute path
WORKDIR /​app

# Better: Clear organization
WORKDIR /​opt/​application

Use COPY Instead of ADD:

Prefer COPY for clarity unless you need ADD's special features:

# Bad: ADD has implicit behavior (extracts tars, fetches URLs)
ADD archive.tar.gz /​app/

# Good: COPY is explicit and predictable
COPY src/ /​app/​src/
COPY package.json /​app/

# OK: ADD when you need tar extraction
ADD rootfs.tar.gz /

Docker Compose Best Practices

Structure Compose Files for Environments

Create Environment-Specific Overrides:

Use base compose file with environment overrides:

# docker-compose.yml (base)
version: '3.9'

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    environment:
      NODE_ENV: production
    restart: unless-stopped

  db:
    image: postgres:16-alpine
    volumes:
      - postgres_data:/​var/​lib/​postgresql/​data
    environment:
      POSTGRES_DB: ${DB_NAME}
      POSTGRES_USER: ${DB_USER}
      POSTGRES_PASSWORD: ${DB_PASSWORD}

volumes:
  postgres_data:
# docker-compose.override.yml (development)
version: '3.9'

services:
  app:
    build:
      target: development
    volumes:
      - .:/​app
      - /​app/​node_modules
    ports:
      - "3000:3000"
      - "9229:9229"  # Debug port
    environment:
      NODE_ENV: development

  db:
    ports:
      - "5432:5432"
# docker-compose.prod.yml (production)
version: '3.9'

services:
  app:
    build:
      target: production
    deploy:
      replicas: 3
      resources:
        limits:
          cpus: '0.5'
          memory: 512M
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

Use with:

# Development (uses docker-compose.override.yml automatically)
docker compose up

# Production
docker compose -f docker-compose.yml -f docker-compose.prod.yml up

Implement Dependency Management

Control Startup Order:

Use depends_on with health checks:

version: '3.9'

services:
  app:
    image: myapp:latest
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started

  db:
    image: postgres:16-alpine
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 10s
      timeout: 5s
      retries: 5

  redis:
    image: redis:7-alpine
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 3s
      retries: 5

Configure Resource Limits

Set Memory and CPU Constraints:

Prevent resource exhaustion:

version: '3.9'

services:
  app:
    image: myapp:latest
    deploy:
      resources:
        limits:
          cpus: '1.0'
          memory: 1G
        reservations:
          cpus: '0.5'
          memory: 512M
    mem_limit: 1g
    cpus: 1.0

Build Optimization Strategies

Implement Build Arguments

Parameterize Builds:

Use ARG for build-time configuration:

ARG NODE_VERSION=20
FROM node:${NODE_VERSION}-alpine

ARG BUILD_DATE
ARG VERSION
ARG REVISION

LABEL org.opencontainers.image.created="${BUILD_DATE}" \
      org.opencontainers.image.version="${VERSION}" \
      org.opencontainers.image.revision="${REVISION}"

ARG NODE_ENV=production
ENV NODE_ENV=${NODE_ENV}

WORKDIR /​app
COPY . .
RUN npm ci --only=${NODE_ENV}

CMD ["node", "server.js"]

Build with arguments:

docker build \
  --build-arg NODE_VERSION=20 \
  --build-arg VERSION=1.2.3 \
  --build-arg BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ") \
  --build-arg REVISION=$(git rev-parse --short HEAD) \
  -t myapp:1.2.3 .

Use Multi-Platform Builds

Build for Multiple Architectures:

Create images for AMD64 and ARM64:

# Create buildx builder
docker buildx create --name multiarch --use

# Build for multiple platforms
docker buildx build \
  --platform linux/​amd64,linux/​arm64 \
  --tag ghcr.io/​org/​app:latest \
  --push .

Platform-specific optimizations:

FROM --platform=$BUILDPLATFORM node:20-alpine AS builder
ARG TARGETPLATFORM
ARG BUILDPLATFORM

RUN echo "Building on $BUILDPLATFORM for $TARGETPLATFORM"

WORKDIR /​app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:20-alpine
COPY --from=builder /​app/​dist ./​dist
CMD ["node", "dist/​main.js"]

Performance Optimization

Minimize Image Size

Remove Unnecessary Files:

Clean up in the same layer:

FROM ubuntu:22.04

RUN apt-get update && \
    apt-get install -y --no-install-recommends \
    build-essential \
    python3 && \
    # Build your app here
    apt-get purge -y build-essential && \
    apt-get autoremove -y && \
    apt-get clean && \
    rm -rf /​var/​lib/​apt/​lists/​* /​tmp/​* /​var/​tmp/​*

Analyze Image Layers:

Use dive to inspect layers:

# Install dive
brew install dive

# Analyze image
dive myapp:latest

Implement Parallel Builds

Speed Up Multi-Stage Builds:

Use multiple FROM statements for parallel builds:

# These stages build in parallel
FROM node:20 AS deps
WORKDIR /​app
COPY package*.json ./
RUN npm ci

FROM node:20 AS builder
WORKDIR /​app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:20 AS tester
WORKDIR /​app
COPY --from=deps /​app/​node_modules ./​node_modules
COPY . .
RUN npm test

# Final stage depends on builder and tester
FROM node:20-alpine
WORKDIR /​app
COPY --from=builder /​app/​dist ./​dist
COPY --from=deps /​app/​node_modules ./​node_modules
CMD ["node", "dist/​main.js"]

Official References

Related Skills

  • Container Security - Hardening and vulnerability management
  • Docker Skill - Container runtime operations
  • DevOps Practices - CI/​CD integration and deployment

More skills from majiayu000

xiaohongshu logo
majiayu000/claude-arsenal

xiaohongshu

xiaohongshu

286 148
View
agent-task-conductor logo
majiayu000/claude-skill-registry

agent-task-conductor

Conduct multi-agent task orchestration and workflow coordination.

663 1
View
conductor-setup logo
majiayu000/claude-skill-registry

conductor-setup

Initialize project with Conductor artifacts (product definition,

663 1
View
animation-designer logo
majiayu000/claude-skill-registry

animation-designer

Expert in web animations, transitions, and motion design using Framer Motion and CSS

663 1
View
diagramming logo
majiayu000/claude-skill-registry

diagramming

Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...

663 1
View
h3-pg logo
majiayu000/claude-skill-registry-data

h3-pg

PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.

23 1
View
conductor-development logo
majiayu000/claude-skill-registry

conductor-development

Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...

663 1
View
conductor-status logo
majiayu000/claude-skill-registry

conductor-status

Display project status, active tracks, and next actions

663 1
View
dockerization logo
majiayu000/claude-skill-registry

dockerization

Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.

663 1
View

Popular AI tools

Kaiber logo
Video

Kaiber

Generate, edit, and beat-sync AI video with leading models in one workspace.

Paid
View
Vimcal logo
Productivity

Vimcal

The world's fastest calendar for remote work

Free
View

Transform Your Design with AI Designer by ImgCreator.ai

Freemium
View
Akool AI logo
Content & writing

Akool AI

Revolutionizing Video Production with AI-Powered Creativity

Paid
View

Extend an image past the frame and let AI fill the new aspect ratio.

Freemium
View
StarByFace logo
Security

StarByFace

Discover your celebrity doppelgänger with StarByFace!

Free
View
C

ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.

Freemium
View
Opus Clip logo
Coding & apps

Opus Clip

Opus.ai: Revolutionize Your Web Experience

Free
View