home / skills / eddiebe147 / claude-settings / docker-composer

docker-composer skill

/skills/docker-composer

This skill helps you design Dockerfiles and compose files for efficient multi-container deployments and secure, production-ready containers.

npx playbooks add skill eddiebe147/claude-settings --skill docker-composer

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
14.6 KB
---
name: docker-composer
description: Expert guide for creating Docker Compose configurations, Dockerfiles, and container orchestration. Use when containerizing applications, setting up development environments, or configuring multi-container deployments.
---

# Docker Composer Skill

## Overview

This skill helps you create efficient Docker configurations for development and production. Covers Dockerfiles, Docker Compose, multi-stage builds, networking, volumes, and container orchestration best practices.

## Docker Philosophy

### Container Principles
1. **One process per container**: Keep containers focused
2. **Immutable infrastructure**: Don't modify running containers
3. **Stateless containers**: Store state in volumes or external services
4. **Minimal images**: Smaller = faster + more secure

### Best Practices
- **DO**: Use multi-stage builds for production
- **DO**: Pin specific versions for dependencies
- **DO**: Use `.dockerignore` to exclude unnecessary files
- **DO**: Run as non-root user
- **DON'T**: Store secrets in images or Dockerfiles
- **DON'T**: Use `latest` tag in production
- **DON'T**: Install unnecessary packages

## Dockerfile Patterns

### Node.js Production Dockerfile

```dockerfile
# Dockerfile
# ============================================
# Stage 1: Dependencies
# ============================================
FROM node:20-alpine AS deps
WORKDIR /app

# Install dependencies only when needed
COPY package.json package-lock.json ./
RUN npm ci --only=production

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

COPY package.json package-lock.json ./
RUN npm ci

COPY . .

# Build application
ENV NEXT_TELEMETRY_DISABLED 1
RUN npm run build

# ============================================
# Stage 3: Runner (Production)
# ============================================
FROM node:20-alpine AS runner
WORKDIR /app

ENV NODE_ENV production
ENV NEXT_TELEMETRY_DISABLED 1

# Create non-root user
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs

# Copy built assets
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static

USER nextjs

EXPOSE 3000

ENV PORT 3000
ENV HOSTNAME "0.0.0.0"

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

### Python Production Dockerfile

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

WORKDIR /app

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

# Create virtual environment
RUN python -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"

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

# ============================================
# Stage 2: Runner
# ============================================
FROM python:3.11-slim AS runner

WORKDIR /app

# Create non-root user
RUN groupadd --gid 1000 appgroup \
    && useradd --uid 1000 --gid appgroup --shell /bin/bash appuser

# Copy virtual environment
COPY --from=builder /opt/venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"

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

USER appuser

EXPOSE 8000

CMD ["gunicorn", "--bind", "0.0.0.0:8000", "app:app"]
```

### Development Dockerfile

```dockerfile
# Dockerfile.dev
FROM node:20-alpine

WORKDIR /app

# Install development dependencies
RUN apk add --no-cache git

# Copy package files first (for caching)
COPY package.json package-lock.json ./

# Install all dependencies (including devDependencies)
RUN npm install

# Don't copy files - mount as volume for hot reload
# COPY . .

EXPOSE 3000

CMD ["npm", "run", "dev"]
```

## Docker Compose Configurations

### Full-Stack Development

```yaml
# docker-compose.yml
version: '3.8'

services:
  # ===================
  # Application
  # ===================
  app:
    build:
      context: .
      dockerfile: Dockerfile.dev
    ports:
      - "3000:3000"
    volumes:
      - .:/app
      - /app/node_modules  # Exclude node_modules
    environment:
      - NODE_ENV=development
      - DATABASE_URL=postgresql://postgres:postgres@db:5432/myapp
      - REDIS_URL=redis://redis:6379
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started
    networks:
      - app-network

  # ===================
  # Database
  # ===================
  db:
    image: postgres:15-alpine
    ports:
      - "5432:5432"
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: myapp
    volumes:
      - postgres_data:/var/lib/postgresql/data
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 5s
      retries: 5
    networks:
      - app-network

  # ===================
  # Redis Cache
  # ===================
  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data
    command: redis-server --appendonly yes
    networks:
      - app-network

  # ===================
  # Admin Tools
  # ===================
  adminer:
    image: adminer
    ports:
      - "8080:8080"
    depends_on:
      - db
    networks:
      - app-network

volumes:
  postgres_data:
  redis_data:

networks:
  app-network:
    driver: bridge
```

### Production Configuration

```yaml
# docker-compose.prod.yml
version: '3.8'

services:
  app:
    image: myapp:${VERSION:-latest}
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=production
    env_file:
      - .env.production
    deploy:
      replicas: 3
      resources:
        limits:
          cpus: '0.5'
          memory: 512M
        reservations:
          cpus: '0.25'
          memory: 256M
      restart_policy:
        condition: on-failure
        delay: 5s
        max_attempts: 3
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"
    networks:
      - app-network
    depends_on:
      - db
      - redis

  db:
    image: postgres:15-alpine
    environment:
      POSTGRES_USER: ${DB_USER}
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password
      POSTGRES_DB: ${DB_NAME}
    secrets:
      - db_password
    volumes:
      - postgres_data:/var/lib/postgresql/data
    deploy:
      resources:
        limits:
          cpus: '1'
          memory: 1G
    networks:
      - app-network

  redis:
    image: redis:7-alpine
    command: redis-server --requirepass ${REDIS_PASSWORD}
    volumes:
      - redis_data:/data
    networks:
      - app-network

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./certs:/etc/nginx/certs:ro
    depends_on:
      - app
    networks:
      - app-network

secrets:
  db_password:
    file: ./secrets/db_password.txt

volumes:
  postgres_data:
  redis_data:

networks:
  app-network:
    driver: overlay
```

### Development Override Pattern

```yaml
# docker-compose.override.yml (auto-loaded with docker-compose.yml)
version: '3.8'

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile.dev
    volumes:
      - .:/app
      - /app/node_modules
    environment:
      - DEBUG=true
      - LOG_LEVEL=debug
    command: npm run dev

  db:
    ports:
      - "5432:5432"  # Expose for local tools

  redis:
    ports:
      - "6379:6379"  # Expose for local tools
```

## Advanced Patterns

### Multi-Service Monorepo

```yaml
# docker-compose.yml
version: '3.8'

services:
  # Frontend
  web:
    build:
      context: .
      dockerfile: apps/web/Dockerfile
    ports:
      - "3000:3000"
    environment:
      - API_URL=http://api:4000
    depends_on:
      - api
    networks:
      - frontend
      - backend

  # Backend API
  api:
    build:
      context: .
      dockerfile: apps/api/Dockerfile
    ports:
      - "4000:4000"
    environment:
      - DATABASE_URL=postgresql://postgres:postgres@db:5432/myapp
      - REDIS_URL=redis://redis:6379
    depends_on:
      - db
      - redis
    networks:
      - backend

  # Background Workers
  worker:
    build:
      context: .
      dockerfile: apps/worker/Dockerfile
    environment:
      - DATABASE_URL=postgresql://postgres:postgres@db:5432/myapp
      - REDIS_URL=redis://redis:6379
    depends_on:
      - db
      - redis
    deploy:
      replicas: 2
    networks:
      - backend

  # Shared services
  db:
    image: postgres:15-alpine
    volumes:
      - postgres_data:/var/lib/postgresql/data
    networks:
      - backend

  redis:
    image: redis:7-alpine
    volumes:
      - redis_data:/data
    networks:
      - backend

networks:
  frontend:
  backend:

volumes:
  postgres_data:
  redis_data:
```

### Local Services Stack

```yaml
# docker-compose.services.yml
# Run local versions of external services for development
version: '3.8'

services:
  # Local S3-compatible storage
  minio:
    image: minio/minio
    ports:
      - "9000:9000"
      - "9001:9001"  # Console
    volumes:
      - minio_data:/data
    environment:
      MINIO_ROOT_USER: minioadmin
      MINIO_ROOT_PASSWORD: minioadmin
    command: server /data --console-address ":9001"

  # Local email testing
  mailhog:
    image: mailhog/mailhog
    ports:
      - "1025:1025"  # SMTP
      - "8025:8025"  # Web UI

  # Local Stripe webhooks
  stripe-cli:
    image: stripe/stripe-cli
    command: listen --api-key ${STRIPE_SECRET_KEY} --forward-to http://app:3000/api/webhooks/stripe
    depends_on:
      - app

  # Elasticsearch
  elasticsearch:
    image: elasticsearch:8.11.0
    ports:
      - "9200:9200"
    environment:
      - discovery.type=single-node
      - xpack.security.enabled=false
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    volumes:
      - elasticsearch_data:/usr/share/elasticsearch/data

  # Kibana (Elasticsearch UI)
  kibana:
    image: kibana:8.11.0
    ports:
      - "5601:5601"
    environment:
      ELASTICSEARCH_HOSTS: http://elasticsearch:9200
    depends_on:
      - elasticsearch

volumes:
  minio_data:
  elasticsearch_data:
```

### Testing Configuration

```yaml
# docker-compose.test.yml
version: '3.8'

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile.test
    environment:
      - NODE_ENV=test
      - DATABASE_URL=postgresql://postgres:postgres@db:5432/myapp_test
    depends_on:
      db:
        condition: service_healthy
    command: npm run test:ci

  db:
    image: postgres:15-alpine
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: myapp_test
    tmpfs:
      - /var/lib/postgresql/data  # Use tmpfs for speed
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 2s
      timeout: 5s
      retries: 5

  # E2E testing
  playwright:
    image: mcr.microsoft.com/playwright:v1.40.0-focal
    volumes:
      - .:/app
      - /app/node_modules
    working_dir: /app
    environment:
      - CI=true
      - BASE_URL=http://app:3000
    depends_on:
      - app
    command: npx playwright test
```

## .dockerignore

```dockerignore
# .dockerignore

# Dependencies
node_modules
npm-debug.log
yarn-error.log

# Build output
.next
dist
build
out

# Development
.git
.gitignore
*.md
!README.md

# IDE
.vscode
.idea
*.swp
*.swo

# Environment
.env
.env.*
!.env.example

# Tests
coverage
*.test.js
*.spec.js
__tests__
e2e
playwright-report

# Docker
Dockerfile*
docker-compose*
.docker

# Misc
.DS_Store
*.log
tmp
```

## Docker Commands Reference

### Development Workflow

```bash
# Start development environment
docker-compose up -d

# View logs
docker-compose logs -f app

# Rebuild after package changes
docker-compose up -d --build

# Run one-off commands
docker-compose exec app npm run migrate
docker-compose exec app npm run seed

# Stop everything
docker-compose down

# Stop and remove volumes (reset database)
docker-compose down -v
```

### Production Workflow

```bash
# Build production image
docker build -t myapp:1.0.0 .

# Run with production config
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d

# Scale services
docker-compose up -d --scale app=3

# Rolling update
docker-compose pull app
docker-compose up -d --no-deps app
```

### Debugging

```bash
# Shell into running container
docker-compose exec app sh

# Inspect container
docker inspect <container_id>

# View resource usage
docker stats

# Clean up unused resources
docker system prune -a

# View networks
docker network ls
docker network inspect app-network
```

## Health Checks

### Application Health Check

```typescript
// src/app/api/health/route.ts
import { NextResponse } from 'next/server';

export async function GET() {
  const checks = {
    uptime: process.uptime(),
    timestamp: new Date().toISOString(),
    database: false,
    redis: false,
  };

  try {
    // Check database
    await db.execute('SELECT 1');
    checks.database = true;
  } catch (e) {
    console.error('Database health check failed:', e);
  }

  try {
    // Check Redis
    await redis.ping();
    checks.redis = true;
  } catch (e) {
    console.error('Redis health check failed:', e);
  }

  const isHealthy = checks.database && checks.redis;

  return NextResponse.json(checks, {
    status: isHealthy ? 200 : 503
  });
}
```

### Docker Health Check

```dockerfile
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
  CMD curl -f http://localhost:3000/api/health || exit 1
```

## Checklist

### Dockerfile
- [ ] Multi-stage build for production
- [ ] Non-root user
- [ ] Minimal base image (alpine when possible)
- [ ] Layer caching optimized (dependencies before code)
- [ ] Health check defined
- [ ] `.dockerignore` configured

### Docker Compose
- [ ] Services have health checks
- [ ] Volumes for persistent data
- [ ] Networks for service isolation
- [ ] Resource limits defined
- [ ] Restart policies configured
- [ ] Environment variables externalized

### Security
- [ ] No secrets in Dockerfile or docker-compose
- [ ] Images scanned for vulnerabilities
- [ ] Minimal privileges (no root)
- [ ] Network isolation between services

## When to Use This Skill

Invoke this skill when:
- Containerizing a new application
- Setting up development environments with Docker
- Creating multi-service architectures
- Optimizing Docker builds
- Debugging container issues
- Setting up CI/CD pipelines with Docker
- Migrating from docker-compose to Kubernetes

Overview

This skill is an expert guide for creating Dockerfiles, Docker Compose configurations, and orchestrating multi-container deployments for development and production. It focuses on practical, secure, and efficient container patterns including multi-stage builds, health checks, networking, volumes, and secrets. Use it to standardize container builds, speed local development, and prepare reliable production deployments.

How this skill works

I provide concrete Dockerfile patterns for Node.js and Python, development vs production images, and multi-stage builds to minimize image size and surface attack surface. I supply Compose files for full-stack development, production overlays, testing, monorepo setups, and local service stacks, plus a .dockerignore template and command references for common workflows. I also include health check examples, debugging commands, and a checklist to validate best practices.

When to use it

  • Containerizing a web app for development and production
  • Setting up repeatable local development with hot-reload and mounted volumes
  • Defining multi-service stacks (databases, caches, workers, proxies)
  • Preparing CI/test environments with ephemeral databases and tmpfs
  • Hardening images and deployments for production (secrets, non-root users)

Best practices

  • Use multi-stage builds to keep production images minimal and secure
  • Pin dependency versions and use .dockerignore to reduce build context
  • Run processes as a non-root user and avoid storing secrets in images
  • Define healthchecks and resource limits for production services
  • Avoid the latest tag in production; use explicit versions or CI-provided tags

Example use cases

  • Dev stack: Dockerfile.dev + docker-compose.yml for hot reload and local DB/Redis
  • Production: multi-stage Dockerfile + docker-compose.prod.yml with secrets and scaling
  • Monorepo: separate service Dockerfiles and a composite Compose file for frontend, API, and workers
  • CI/testing: ephemeral Postgres in tmpfs and a test container executing test runner
  • Local service replacements: Minio, MailHog, and Elasticsearch for offline integration testing

FAQ

Should I include secrets in my Dockerfile or image?

No. Use Docker secrets, environment files excluded from the image, or secret managers. Never bake secrets into images or commit them to version control.

When should I use multi-stage builds?

Always for production images where build tools and dev dependencies are unnecessary at runtime. Multi-stage builds reduce size and improve security.