Back to catalog

dockerfile-optimizer

Create optimized Dockerfiles with multi-stage builds, layer caching, security best practices. Docker, Docker Compose. Keywords - dockerfile, docker optimization, multi stage build, docker best practices, container, image size, docker compose

  • Category: discovered
  • Tags: github, community
  • Version: 1.0.0
  • Author: walid
  • Downloads: 0
  • Updated: 2025-11-22

Download the latest version (v1.0.0) from this page.

SKILL.md

---
name: dockerfile-optimizer
description: Create optimized Dockerfiles with multi-stage builds, layer caching, security best practices. Docker, Docker Compose. Keywords - dockerfile, docker optimization, multi stage build, docker best practices, container, image size, docker compose
---

# Dockerfile Optimizer

## When to Use This Skill

Activate when:
- Creating Docker images for applications
- Need to reduce image size
- Want faster Docker builds
- Implementing multi-stage builds
- Setting up Docker Compose

## What This Skill Does

Creates optimized Dockerfiles with:
- Multi-stage builds (reduce image size)
- Layer caching optimization
- Minimal base images (Alpine)
- Security best practices (non-root user)
- Health checks
- .dockerignore for faster builds

## Supported Technologies

**Base Images**:
- Node/Bun: `node:alpine`, `oven/bun:alpine`
- Python: `python:alpine`, `python:slim`
- Multi-stage: Builder + Runtime

## Example: Optimized Node.js Dockerfile (Multi-Stage)

```dockerfile
# Dockerfile (Node.js + TypeScript)

# ==========================================
# Stage 1: Dependencies
# ==========================================
FROM node:20-alpine AS deps

WORKDIR /app

# Copy package files
COPY package.json package-lock.json ./

# Install dependencies (layer cached)
RUN npm ci --only=production && \
    npm cache clean --force

# ==========================================
# Stage 2: Builder
# ==========================================
FROM node:20-alpine AS builder

WORKDIR /app

# Copy package files
COPY package.json package-lock.json ./

# Install ALL dependencies (including devDependencies)
RUN npm ci

# Copy source code
COPY . .

# Build TypeScript
RUN npm run build

# ==========================================
# Stage 3: Runtime
# ==========================================
FROM node:20-alpine AS runtime

# Install dumb-init (proper signal handling)
RUN apk add --no-cache dumb-init

# Create non-root user
RUN addgroup -g 1001 -S nodejs && \
    adduser -S nodejs -u 1001

# Set working directory
WORKDIR /app

# Copy dependencies from deps stage
COPY --from=deps --chown=nodejs:nodejs /app/node_modules ./node_modules

# Copy build output from builder stage
COPY --from=builder --chown=nodejs:nodejs /app/dist ./dist
COPY --chown=nodejs:nodejs package.json ./

# Switch to non-root user
USER nodejs

# Expose port
EXPOSE 3000

# Health check
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
  CMD node -e "require('http').get('http://localhost:3000/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"

# Use dumb-init to handle signals properly
ENTRYPOINT ["dumb-init", "--"]

# Start application
CMD ["node", "dist/index.js"]
```

## Example: Python Dockerfile (Multi-Stage)

```dockerfile
# Dockerfile (Python + FastAPI)

# ==========================================
# Stage 1: Builder
# ==========================================
FROM python:3.11-slim AS builder

WORKDIR /app

# Install system dependencies
RUN apt-get update && \
    apt-get install -y --no-install-recommends gcc && \
    rm -rf /var/lib/apt/lists/*

# Copy requirements
COPY requirements.txt ./

# Install Python dependencies
RUN pip install --no-cache-dir --user -r requirements.txt

# ==========================================
# Stage 2: Runtime
# ==========================================
FROM python:3.11-slim AS runtime

# Create non-root user
RUN useradd -m -u 1001 appuser

WORKDIR /app

# Copy Python dependencies from builder
COPY --from=builder --chown=appuser:appuser /root/.local /home/appuser/.local

# Copy application code
COPY --chown=appuser:appuser . .

# Update PATH for user-installed packages
ENV PATH=/home/appuser/.local/bin:$PATH

# Switch to non-root user
USER appuser

# Expose port
EXPOSE 8000

# Health check
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
  CMD python -c "import requests; requests.get('http://localhost:8000/health').raise_for_status()"

# Start application
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
```

## Example: Bun Dockerfile

```dockerfile
# Dockerfile (Bun + TypeScript)

# ==========================================
# Stage 1: Dependencies
# ==========================================
FROM oven/bun:1-alpine AS deps

WORKDIR /app

COPY package.json bun.lockb ./

RUN bun install --frozen-lockfile --production

# ==========================================
# Stage 2: Builder
# ==========================================
FROM oven/bun:1-alpine AS builder

WORKDIR /app

COPY package.json bun.lockb ./
RUN bun install --frozen-lockfile

COPY . .
RUN bun run build

# ==========================================
# Stage 3: Runtime
# ==========================================
FROM oven/bun:1-alpine AS runtime

RUN addgroup -g 1001 -S bun && \
    adduser -S bun -u 1001

WORKDIR /app

COPY --from=deps --chown=bun:bun /app/node_modules ./node_modules
COPY --from=builder --chown=bun:bun /app/dist ./dist
COPY --chown=bun:bun package.json ./

USER bun

EXPOSE 3000

HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
  CMD bun -e "require('http').get('http://localhost:3000/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"

CMD ["bun", "run", "dist/index.js"]
```

## Example: .dockerignore

```gitignore
# .dockerignore

# Version control
.git
.gitignore

# Dependencies (will be installed in container)
node_modules
venv
__pycache__

# Development files
.env
.env.local
.env.*.local

# Build output (will be built in container)
dist
build
*.log

# IDE
.vscode
.idea
*.swp

# Tests
tests
*.test.ts
*.spec.ts
coverage

# Documentation
README.md
docs
*.md

# CI/CD
.github
.gitlab-ci.yml
Jenkinsfile
```

## Example: Docker Compose (Development)

```yaml
# docker-compose.yml

version: '3.9'

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
      target: development  # Use development stage
    ports:
      - '3000:3000'
    volumes:
      - .:/app  # Mount source code for hot reload
      - /app/node_modules  # Don't override node_modules
    environment:
      - NODE_ENV=development
      - DATABASE_URL=postgresql://user:password@db:5432/myapp
      - REDIS_URL=redis://redis:6379
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started
    command: npm run dev

  db:
    image: postgres:15-alpine
    ports:
      - '5432:5432'
    environment:
      - POSTGRES_USER=user
      - POSTGRES_PASSWORD=password
      - POSTGRES_DB=myapp
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ['CMD-SHELL', 'pg_isready -U user']
      interval: 10s
      timeout: 5s
      retries: 5

  redis:
    image: redis:7-alpine
    ports:
      - '6379:6379'
    volumes:
      - redis_data:/data
    command: redis-server --appendonly yes

volumes:
  postgres_data:
  redis_data:
```

## Best Practices

1. **Multi-stage builds** - Separate builder and runtime
2. **Alpine images** - Smaller base images (~5MB vs 900MB)
3. **Layer caching** - Copy package files first, then source
4. **Non-root user** - Security best practice
5. **Health checks** - Docker can restart unhealthy containers
6. **.dockerignore** - Faster builds, smaller context
7. **Minimal dependencies** - Only install what's needed

## Image Size Comparison

| Approach | Image Size |
|----------|------------|
| node:20 (full) | ~950 MB |
| node:20-slim | ~250 MB |
| node:20-alpine | ~180 MB |
| node:20-alpine (multi-stage) | **~100 MB** ✅ |

## Security Best Practices

```dockerfile
# ✅ GOOD: Security best practices

# Use specific version (not latest)
FROM node:20-alpine

# Create non-root user
RUN adduser -D appuser
USER appuser

# Don't run as root
# Don't expose unnecessary ports
# Don't include secrets in image
# Scan for vulnerabilities: docker scan myimage
```

## Example: Development vs Production Dockerfile

```dockerfile
# Dockerfile

# ==========================================
# Development Stage
# ==========================================
FROM node:20-alpine AS development

WORKDIR /app

COPY package*.json ./
RUN npm install  # Include devDependencies

COPY . .

EXPOSE 3000
CMD ["npm", "run", "dev"]

# ==========================================
# Production Stage
# ==========================================
FROM node:20-alpine AS production

WORKDIR /app

# Install production dependencies only
COPY package*.json ./
RUN npm ci --only=production

# Copy source code
COPY . .

# Build
RUN npm run build

# Create non-root user
RUN adduser -D appuser
USER appuser

EXPOSE 3000
CMD ["node", "dist/index.js"]
```

```bash
# Build development image
docker build --target development -t myapp:dev .

# Build production image
docker build --target production -t myapp:prod .
```

## Integration with Other Skills

- **api-endpoint-builder** - Dockerize APIs
- **health-check-builder** - Add health checks to Dockerfile
- **environment-config-validator** - Validate env in container

---

**Version**: 1.0.0
**Category**: Testing + DevOps
**Complexity**: Medium