Skip to main content
skill CX Codex

docker · bobmatnyc — Claude Code skill: Dockerfiles and Compose

A Docker skill that helps your agent write Dockerfiles, build images and run services with Compose. Useful when the same app has to behave the same in dev and production.

Install into Claude Code · Skills CLI (Vercel) All methods and platforms ↓
npx skills add bobmatnyc/claude-mpm-skills --skill docker -a claude-code
Open on GitHub

What docker does

The skill hands your agent a reference on containerization: Dockerfile instructions, layer ordering for cache hits, multi-stage builds, Docker Compose, debugging and common failures. Sections cover core concepts, multi-stage builds, development workflows, production patterns, framework examples and orchestration. The agent pulls the section it needs instead of the whole document.

There are worked examples: Node.js on node:18-alpine, Python on python:3.11-slim, Go built into scratch. They show how to separate dependencies from code so the cache survives a code change, and how to keep build tooling out of the final image. For multi-container apps there is a docker-compose.yml layout with services, volumes and environment variables.

The skill is non-interactive: user-invocable: false and disable-model-invocation: true. You cannot call it as a slash command; it loads as reference material. Docker itself has to be installed on your machine, since the skill describes working with it rather than installing it.

What it helps with

  • write a Dockerfile with layer order that keeps the cache warm
  • shrink the final image with a multi-stage build
  • bring up an app and a database together with Docker Compose
  • get hot reload working inside a container during development
  • figure out why a container crashes or cannot reach another service

Example prompts for the agent

  • write a multi-stage Dockerfile for my Node.js app
  • create a docker-compose.yml with app and postgres, add a volume for the data
  • why do my dependencies reinstall on every build — check the Dockerfile

How to install docker

1Skills CLI (Vercel)

npx skills add bobmatnyc/claude-mpm-skills --skill docker -a claude-code

2Manually: put the files in place

~/.claude/skills/docker/SKILL.md

Copy the whole skill folder; for one project use .claude/skills/docker/

Step by step, with commands for each platform: How to install skills in Claude Code: five ways

Questions about docker

How do I install the docker skill in Claude?

Run npx skills add bobmatnyc/claude-mpm-skills --skill docker -a claude-code. The skill file lands at ~/.claude/skills/docker/SKILL.md.

What does the docker skill do?

It gives your agent reference material on containerization: Dockerfiles, multi-stage builds, Compose, debugging and common errors. The agent reads the relevant section when you ask for Docker work.

Can I invoke the skill manually?

No. It is marked user-invocable: false and disable-model-invocation: true, so there is no slash command. It works as a reference the agent reaches for on its own.

Source text

SKILL.md · MIT
---
name: docker
description: Docker containerization for packaging applications with dependencies into isolated, portable units ensuring consistency across development, testing, and production environments.
user-invocable: false
disable-model-invocation: true
progressive_disclosure:
  entry_point:
    - summary
    - when_to_use
    - quick_start
  sections:
    - core_concepts
    - dockerfile_basics
    - multi_stage_builds
    - docker_compose
    - development_workflows
    - production_patterns
    - framework_examples
    - orchestration
    - debugging
    - troubleshooting
    - best_practices
---

# Docker Containerization Skill

## Summary
Docker provides containerization for packaging applications with their dependencies into isolated, portable units. Containers ensure consistency across development, testing, and production environments, eliminating "works on my machine" problems.

## When to Use
- **Local Development**: Consistent dev environments across team members
- **CI/CD Pipelines**: Reproducible build and test environments
- **Microservices**: Isolated services with independent scaling
- **Production Deployment**: Portable applications across cloud providers
- **Database/Service Testing**: Ephemeral databases for integration tests
- **Legacy Application Isolation**: Run incompatible dependencies side-by-side

## Quick Start

### 1. Create Dockerfile
```dockerfile
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]
```

### 2. Build Image
```bash
docker build -t myapp:1.0 .
```

### 3. Run Container
```bash
docker run -p 3000:3000 myapp:1.0
```

---

## Core Concepts

### Images vs Containers
- **Image**: Read-only template with application code, runtime, and dependencies
- **Container**: Running instance of an image with writable layer
- **Registry**: Storage for images (Docker Hub, GitHub Container Registry)

### Layers and Caching
Each Dockerfile instruction creates a layer. Docker caches unchanged layers for faster builds.

```dockerfile
# GOOD: Dependencies change less frequently than code
FROM python:3.11-slim
COPY requirements.txt .
RUN pip install -r requirements.txt  # Cached unless requirements.txt changes
COPY . .                              # Rebuild only when code changes

# BAD: Invalidates cache on every code change
FROM python:3.11-slim
COPY . .                              # Changes frequently
RUN pip install -r requirements.txt  # Reinstalls on every build
```

### Volumes
Persistent data storage that survives container restarts.

```bash
# Named volume (managed by Docker)
docker run -v mydata:/app/data myapp

# Bind mount (host directory)
docker run -v $(pwd)/data:/app/data myapp

# Anonymous volume (temporary)
docker run -v /app/data myapp
```

### Networks
Containers communicate through Docker networks.

```bash
# Create network
docker network create mynetwork

# Run containers on network
docker run --network mynetwork --name db postgres
docker run --network mynetwork --name app myapp
# App can connect to db using hostname "db"
```

---

## Dockerfile Basics

### Essential Instructions

```dockerfile
# Base image
FROM node:18-alpine

# Metadata
LABEL maintainer="[email protected]"
LABEL version="1.0"

# Set working directory
WORKDIR /app

# Copy files
COPY package*.json ./
COPY src/ ./src/

# Run commands (creates layer)
RUN npm ci --only=production

# Set environment variables
ENV NODE_ENV=production
ENV PORT=3000

# Expose ports (documentation only)
EXPOSE 3000

# Default command
CMD ["node", "src/server.js"]

# Alternative: ENTRYPOINT (not overridden by docker run args)
ENTRYPOINT ["node"]
CMD ["src/server.js"]  # Default args for ENTRYPOINT
```

### Instruction Order for Cache Efficiency

```dockerfile
# 1. Base image (rarely changes)
FROM python:3.11-slim

# 2. System dependencies (rarely change)
RUN apt-get update && apt-get install -y \
    gcc \
    && rm -rf /var/lib/apt/lists/*

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

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

# 5. Runtime configuration
ENV PYTHONUNBUFFERED=1
EXPOSE 8000
CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]
```

### .dockerignore
Exclude files from build context (faster builds, smaller images).

```
# .dockerignore
node_modules/
npm-debug.log
.git/
.gitignore
*.md
.env
.vscode/
__pycache__/
*.pyc
.pytest_cache/
coverage/
dist/
build/
```

---

## Multi-Stage Builds

Optimize image size by separating build and runtime stages.

### Node.js TypeScript Example

```dockerfile
# Build stage
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# Production stage
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY --from=builder /app/dist ./dist
EXPOSE 3000
CMD ["node", "dist/server.js"]
```

**Benefits**:
- Build dependencies (TypeScript, webpack) excluded from final image
- Final image: ~50MB vs ~500MB with build tools
- Faster deployments and reduced attack surface

### Python Example

```dockerfile
# Build stage
FROM python:3.11 AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --user --no-cache-dir -r requirements.txt

# Runtime stage
FROM python:3.11-slim
WORKDIR /app
COPY --from=builder /root/.local /root/.local
COPY . .
ENV PATH=/root/.local/bin:$PATH
CMD ["python", "app.py"]
```

### Go Example (Smallest Images)

```dockerfile
# Build stage
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY go.* ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o server

# Runtime stage (scratch = empty base image)
FROM scratch
COPY --from=builder /app/server /server
EXPOSE 8080
ENTRYPOINT ["/server"]
```

Result: ~10MB final image containing only the compiled binary.

---

## Docker Compose

Define multi-container applications in YAML.

### Basic Structure

```yaml
version: '3.8'

services:
  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      - DATABASE_URL=postgres://db:5432/myapp
    depends_on:
      - db
    volumes:
      - ./src:/app/src  # Hot reload in development

  db:
    image: postgres:15-alpine
    environment:
      POSTGRES_PASSWORD: secret
      POSTGRES_DB: myapp
    volumes:
      - db_data:/var/lib/postgresql/data
    ports:
      - "5432:5432"

volumes:
  db_data:
```

### Commands

```bash
# Start all services
docker-compose up

# Start in background
docker-compose up -d

# Rebuild images
docker-compose up --build

# Stop services
docker-compose down

# Stop and remove volumes
docker-compose down -v

# View logs
docker-compose logs -f app

# Run one-off command
docker-compose run app npm test
```

### Full Stack Example

```yaml
version: '3.8'

services:
  # Frontend
  web:
    build:
      context: ./frontend
      dockerfile: Dockerfile.dev
    ports:
      - "3000:3000"
    volumes:
      - ./frontend/src:/app/src
    environment:
      - REACT_APP_API_URL=http://localhost:8000

  # Backend API
  api:
    build: ./backend
    ports:
      - "8000:8000"
    environment:
      - DATABASE_URL=postgresql://postgres:secret@db:5432/myapp
      - REDIS_URL=redis://redis:6379
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started
    volumes:
      - ./backend:/app
    command: uvicorn main:app --host 0.0.0.0 --reload

  # Database
  db:
    image: postgres:15-alpine
    environment:
      POSTGRES_PASSWORD: secret
      POSTGRES_DB: myapp
    volumes:
      - db_data:/var/lib/postgresql/data
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 10s
      timeout: 5s
      retries: 5

  # Cache
  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data

  # Worker (background jobs)
  worker:
    build: ./backend
    command: celery -A tasks worker --loglevel=info
    environment:
      - REDIS_URL=redis://redis:6379
    depends_on:
      - redis
      - db

volumes:
  db_data:
  redis_data:

networks:
  default:
    name: myapp_network
```

---

## Development Workflows

### Hot Reload with Volumes

#### Node.js

```yaml
services:
  app:
    build: .
    volumes:
      - ./src:/app/src        # Sync source code
      - /app/node_modules     # Prevent overwriting container's node_modules
    command: npm run dev
```

```dockerfile
# Dockerfile.dev
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install  # Include dev dependencies
COPY . .
EXPOSE 3000
CMD ["npm", "run", "dev"]
```

#### Python (Django/FastAPI)

```yaml
services:
  web:
    build: .
    volumes:
      - .:/app
    command: python manage.py runserver 0.0.0.0:8000
    # or for FastAPI:
    # command: uvicorn main:app --host 0.0.0.0 --reload
```

### VS Code Dev Containers

`.devcontainer/devcontainer.json`:

```json
{
  "name": "Python Dev Container",
  "dockerComposeFile": "../docker-compose.yml",
  "service": "app",
  "workspaceFolder": "/app",
  "customizations": {
    "vscode": {
      "extensions": [
        "ms-python.python",
        "ms-python.vscode-pylance"
      ],
      "settings": {
        "python.defaultInterpreterPath": "/usr/local/bin/python"
      }
    }
  },
  "postCreateCommand": "pip install -r requirements-dev.txt",
  "remoteUser": "vscode"
}
```

### Local Database Containers

```bash
# PostgreSQL
docker run -d \
  --name dev-postgres \
  -e POSTGRES_PASSWORD=localdev \
  -e POSTGRES_DB=myapp_dev \
  -p 5432:5432 \
  -v pgdata:/var/lib/postgresql/data \
  postgres:15-alpine

# MySQL
docker run -d \
  --name dev-mysql \
  -e MYSQL_ROOT_PASSWORD=localdev \
  -e MYSQL_DATABASE=myapp_dev \
  -p 3306:3306 \
  -v mysqldata:/var/lib/mysql \
  mysql:8

# MongoDB
docker run -d \
  --name dev-mongo \
  -p 27017:27017 \
  -v mongodata:/data/db \
  mongo:7

# Redis
docker run -d \
  --name dev-redis \
  -p 6379:6379 \
  redis:7-alpine
```

---

## Production Patterns

### Health Checks

```dockerfile
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .

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

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

```javascript
// healthcheck.js
const http = require('http');

const options = {
  host: 'localhost',
  port: 3000,
  path: '/health',
  timeout: 2000
};

const request = http.request(options, (res) => {
  if (res.statusCode === 200) {
    process.exit(0);
  } else {
    process.exit(1);
  }
});

request.on('error', () => process.exit(1));
request.end();
```

### Security Best Practices

```dockerfile
FROM python:3.11-slim

# 1. Use non-root user
RUN groupadd -r appuser && useradd -r -g appuser appuser

# 2. Install dependencies as root
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# 3. Copy application files
COPY --chown=appuser:appuser . .

# 4. Switch to non-root user
USER appuser

# 5. Drop unnecessary privileges
EXPOSE 8000
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "app:app"]
```

**Additional Security Measures**:
- Use minimal base images (alpine, distroless)
- Scan images for vulnerabilities: `docker scan myapp:latest`
- Don't include secrets in images (use environment variables or secret managers)
- Keep base images updated
- Use read-only root filesystem when possible

### Secrets Management

```bash
# Docker Swarm secrets (production)
echo "db_password_here" | docker secret create db_password -
```

```yaml
version: '3.8'
services:
  app:
    image: myapp
    secrets:
      - db_password
    environment:
      - DB_PASSWORD_FILE=/run/secrets/db_password

secrets:
  db_password:
    external: true
```

**Alternative: Environment Files**

```yaml
# docker-compose.yml
services:
  app:
    env_file:
      - .env.production  # Never commit this file
```

```bash
# .env.production (gitignored)
DATABASE_URL=postgresql://user:pass@db:5432/prod
SECRET_KEY=your-secret-key
```

### Resource Limits

```yaml
services:
  app:
    image: myapp
    deploy:
      resources:
        limits:
          cpus: '1.0'
          memory: 512M
        reservations:
          cpus: '0.5'
          memory: 256M
      restart_policy:
        condition: on-failure
        delay: 5s
        max_attempts: 3
```

```bash
# Command-line resource limits
docker run -d \
  --memory="512m" \
  --cpus="1.0" \
  --restart=unless-stopped \
  myapp
```

---

## Framework-Specific Examples

### Python: Django

```dockerfile
FROM python:3.11-slim

ENV PYTHONUNBUFFERED=1
ENV PYTHONDONTWRITEBYTECODE=1

WORKDIR /app

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

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

# Copy application
COPY . .

# Collect static files
RUN python manage.py collectstatic --noinput

# Create non-root user
RUN useradd -m -u 1000 django && chown -R django:django /app
USER django

EXPOSE 8000
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "--workers", "4", "myproject.wsgi:application"]
```

**docker-compose.yml**:

```yaml
version: '3.8'

services:
  web:
    build: .
    command: python manage.py runserver 0.0.0.0:8000
    volumes:
      - .:/app
    ports:
      - "8000:8000"
    environment:
      - DEBUG=1
      - DATABASE_URL=postgres://postgres:postgres@db:5432/django_dev
    depends_on:
      - db

  db:
    image: postgres:15-alpine
    environment:
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: django_dev
    volumes:
      - postgres_data:/var/lib/postgresql/data

volumes:
  postgres_data:
```

### Python: FastAPI

```dockerfile
FROM python:3.11-slim

WORKDIR /app

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

COPY . .

EXPOSE 8000

CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "4"]
```

### Node.js: Next.js

```dockerfile
# Multi-stage build for Next.js
FROM node:18-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci

FROM node:18-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build

FROM node:18-alpine AS runner
WORKDIR /app

ENV NODE_ENV=production

RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001

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

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

**next.config.js** (required for standalone output):

```javascript
module.exports = {
  output: 'standalone',
}
```

### Node.js: Express

```dockerfile
FROM node:18-alpine

WORKDIR /app

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

# Copy application
COPY . .

# Create non-root user
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser

EXPOSE 3000

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

### TypeScript Build

```dockerfile
# Build stage
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json tsconfig.json ./
RUN npm ci
COPY src/ ./src/
RUN npm run build

# Production stage
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY --from=builder /app/dist ./dist

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

---

## Container Orchestration Basics

### Docker Swarm (Built-in)

```bash
# Initialize swarm
docker swarm init

# Deploy stack
docker stack deploy -c docker-compose.yml myapp

# Scale service
docker service scale myapp_web=5

# Update service (zero-downtime)
docker service update --image myapp:2.0 myapp_web

# Remove stack
docker stack rm myapp
```

### Kubernetes Comparison

| Feature | Docker Compose | Docker Swarm | Kubernetes |
|---------|---------------|--------------|------------|
| **Complexity** | Low | Medium | High |
| **Use Case** | Local dev | Small clusters | Production at scale |
| **Setup** | Single file | Built-in | Separate installation |
| **Scaling** | Manual | Automatic | Automatic + Advanced |
| **HA** | No | Yes | Yes |
| **Ecosystem** | Limited | Docker | Massive |

**When to use each**:
- **Docker Compose**: Local development, simple deployments
- **Docker Swarm**: Small production clusters, simpler than K8s
- **Kubernetes**: Large-scale production, multi-cloud, advanced orchestration

---

## CI/CD Integration

### GitHub Actions

```yaml
# .github/workflows/docker.yml
name: Build and Push Docker Image

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  build:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write

    steps:
      - uses: actions/checkout@v3

      - name: Log in to Container Registry
        uses: docker/login-action@v2
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Extract metadata
        id: meta
        uses: docker/metadata-action@v4
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
          tags: |
            type=ref,event=branch
            type=semver,pattern={{version}}
            type=sha

      - name: Build and push
        uses: docker/build-push-action@v4
        with:
          context: .
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

      - name: Run tests
        run: |
          docker run --rm ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} npm test
```

### GitLab CI

```yaml
# .gitlab-ci.yml
stages:
  - build
  - test
  - deploy

variables:
  DOCKER_DRIVER: overlay2
  IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA

build:
  stage: build
  image: docker:latest
  services:
    - docker:dind
  script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
    - docker build -t $IMAGE_TAG .
    - docker push $IMAGE_TAG

test:
  stage: test
  script:
    - docker run --rm $IMAGE_TAG npm test

deploy:
  stage: deploy
  script:
    - docker pull $IMAGE_TAG
    - docker tag $IMAGE_TAG $CI_REGISTRY_IMAGE:latest
    - docker push $CI_REGISTRY_IMAGE:latest
  only:
    - main
```

---

## Debugging Containers

### Viewing Logs

```bash
# Stream logs
docker logs -f container_name

# Last 100 lines
docker logs --tail 100 container_name

# Logs since timestamp
docker logs --since 2024-01-01T10:00:00 container_name

# With timestamps
docker logs -t container_name

# Docker Compose logs
docker-compose logs -f service_name
```

### Execute Commands in Running Container

```bash
# Interactive shell
docker exec -it container_name /bin/sh
# or
docker exec -it container_name /bin/bash

# Run single command
docker exec container_name ls -la /app

# Run as different user
docker exec -u root container_name apt-get update
```

### Inspect Container

```bash
# Full container details
docker inspect container_name

# Specific field (IP address)
docker inspect -f '{{.NetworkSettings.IPAddress}}' container_name

# Environment variables
docker inspect -f '{{.Config.Env}}' container_name

# Mounted volumes
docker inspect -f '{{.Mounts}}' container_name
```

### Resource Usage

```bash
# Real-time stats
docker stats

# Single container
docker stats container_name

# No streaming (single snapshot)
docker stats --no-stream
```

### Network Debugging

```bash
# List networks
docker network ls

# Inspect network
docker network inspect bridge

# Test connectivity between containers
docker exec container1 ping container2

# Check DNS resolution
docker exec container_name nslookup other_container
```

### Debugging Build Issues

```bash
# Build with no cache
docker build --no-cache -t myapp .

# Show build progress
docker build --progress=plain -t myapp .

# Build specific stage
docker build --target builder -t myapp-builder .

# Inspect intermediate layers
docker history myapp:latest
```

---

## Local Docker Patterns (mcp-browser, mcp-memory)

### mcp-browser dev compose

- `docker-compose.yml` runs `mcp-server` with a port range `8875-8895` and optional `chrome` profile.
- Code mounts are read-only (`./src:/app/src:ro`) with persistent logs and temp volumes.
- Environment defaults: `MCP_DEBUG=true`, `MCP_LOG_LEVEL=DEBUG`, `MCP_HOST=0.0.0.0`, `MCP_PORT=8875`.
- Optional profiles: `chrome` (browser) and `tools` (dev tools container).

### mcp-browser Dockerfile.dev

- `ARG PYTHON_VERSION=3.11`, install `watchdog` + Playwright Chromium.
- Install package in editable mode and run `python -m src.dev_runner`.
- Non-root user and healthcheck on `/health`.

### mcp-memory production Dockerfile

- Multi-stage build with venv in `/opt/venv` and `python:3.11-slim`.
- Runtime installs `curl` for healthcheck, sets `PYTHONPATH=/app`.
- Uses non-root user and `CMD ["python", "run_api_server.py"]` with `/health` check.

## Troubleshooting

### Common Issues

#### "Port already in use"

```bash
# Find process using port
lsof -i :3000
# or
netstat -tulpn | grep 3000

# Kill process
kill -9 <PID>

# Or use different host port
docker run -p 3001:3000 myapp
```

#### "Cannot connect to Docker daemon"

```bash
# Check Docker is running
docker info

# Restart Docker Desktop (Mac/Windows)
# or
sudo systemctl restart docker  # Linux

# Check permissions (Linux)
sudo usermod -aG docker $USER
# Log out and back in
```

#### "No space left on device"

```bash
# Remove unused containers, images, volumes
docker system prune -a --volumes

# Remove only dangling images
docker image prune

# Remove stopped containers
docker container prune

# Remove unused volumes
docker volume prune

# Check disk usage
docker system df
```

#### "Build context too large"

```bash
# Create .dockerignore
cat > .dockerignore << EOF
node_modules/
.git/
*.log
dist/
coverage/
EOF

# Build with specific context
docker build -f Dockerfile -t myapp ./src
```

#### Container Exits Immediately

```bash
# Check logs
docker logs container_name

# Run with interactive shell to debug
docker run -it myapp /bin/sh

# Override entrypoint
docker run -it --entrypoint /bin/sh myapp

# Check exit code
docker inspect -f '{{.State.ExitCode}}' container_name
```

#### Permission Denied in Container

```bash
# Run as root to debug
docker exec -u root -it container_name /bin/sh

# Fix ownership
docker exec -u root container_name chown -R appuser:appuser /app

# Or rebuild with correct permissions in Dockerfile
```

### Performance Issues

#### Slow Builds

```bash
# Use BuildKit (faster, better caching)
DOCKER_BUILDKIT=1 docker build -t myapp .

# Multi-stage builds to reduce layers
# Order instructions by change frequency
# Use .dockerignore to exclude unnecessary files
```

#### High Memory Usage

```bash
# Set memory limits
docker run -m 512m myapp

# Monitor memory
docker stats container_name

# Check for memory leaks in application
```

#### Slow Volume Mounts (Mac/Windows)

```bash
# Use delegated consistency (Mac)
volumes:
  - ./src:/app/src:delegated

# Or use named volumes instead of bind mounts
volumes:
  - node_modules:/app/node_modules
```

---

## Best Practices

### Layer Optimization

1. **Order by Change Frequency**: Least frequently changed first
2. **Combine RUN Commands**: Reduce layers
3. **Clean Up in Same Layer**: Remove temp files immediately

```dockerfile
# GOOD
RUN apt-get update && apt-get install -y \
    package1 \
    package2 \
    && rm -rf /var/lib/apt/lists/*

# BAD (creates 3 layers, apt cache remains in layer 2)
RUN apt-get update
RUN apt-get install -y package1 package2
RUN rm -rf /var/lib/apt/lists/*
```

### Image Size Reduction

1. **Use Alpine Images**: 5MB vs 100MB+ for full Linux
2. **Multi-Stage Builds**: Exclude build tools from final image
3. **Remove Unnecessary Files**: Docs, examples, tests
4. **Use .dockerignore**: Exclude development files

```dockerfile
# Before: 800MB
FROM node:18
COPY . .
RUN npm install
CMD ["node", "server.js"]

# After: 120MB
FROM node:18-alpine
COPY package*.json ./
RUN npm ci --only=production
COPY server.js .
CMD ["node", "server.js"]
```

### Security Checklist

- [ ] Use official base images from trusted sources
- [ ] Specify exact image versions (not `latest`)
- [ ] Run as non-root user
- [ ] Scan images for vulnerabilities
- [ ] Keep base images updated
- [ ] Don't embed secrets in images
- [ ] Use read-only root filesystem when possible
- [ ] Minimize attack surface (alpine, distroless)
- [ ] Enable Docker Content Trust (image signing)

### Development vs Production

**Development**:
```dockerfile
FROM node:18
WORKDIR /app
COPY package*.json ./
RUN npm install  # Include dev dependencies
COPY . .
CMD ["npm", "run", "dev"]
```

**Production**:
```dockerfile
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY --from=builder /app/dist ./dist
USER node
CMD ["node", "dist/server.js"]
```

### Logging Best Practices

```dockerfile
# Log to stdout/stderr (Docker captures these)
CMD ["node", "server.js"]  # Good

# Don't log to files (lost when container stops)
CMD ["node", "server.js", ">", "app.log"]  # Bad
```

```javascript
// Application logging
console.log('Info message');   // stdout
console.error('Error message'); // stderr

// Use structured logging
console.log(JSON.stringify({
  level: 'info',
  timestamp: new Date().toISOString(),
  message: 'Request processed',
  requestId: '123'
}));
```

### Environment Configuration

```dockerfile
# Use ARG for build-time variables
ARG NODE_ENV=production
ENV NODE_ENV=$NODE_ENV

# Use ENV for runtime variables
ENV PORT=3000
ENV LOG_LEVEL=info

# Override at runtime
# docker run -e PORT=8080 -e LOG_LEVEL=debug myapp
```

### Health Checks

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

```yaml
# docker-compose.yml
services:
  app:
    image: myapp
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 30s
      timeout: 3s
      retries: 3
      start_period: 40s
```

### Graceful Shutdown

```javascript
// Node.js example
process.on('SIGTERM', () => {
  console.log('SIGTERM received, closing server...');
  server.close(() => {
    console.log('Server closed');
    process.exit(0);
  });
});
```

```dockerfile
# Use exec form to properly handle signals
CMD ["node", "server.js"]  # Good
CMD node server.js          # Bad (wrapped in /bin/sh, signals not forwarded)
```

---

## Quick Reference

### Essential Commands

```bash
# Images
docker build -t name:tag .
docker pull image:tag
docker push image:tag
docker images
docker rmi image:tag

# Containers
docker run -d --name container image
docker ps                    # Running containers
docker ps -a                 # All containers
docker stop container
docker start container
docker restart container
docker rm container
docker logs -f container
docker exec -it container /bin/sh

# Cleanup
docker system prune -a       # Remove all unused resources
docker container prune       # Remove stopped containers
docker image prune          # Remove dangling images
docker volume prune         # Remove unused volumes

# Compose
docker-compose up -d
docker-compose down
docker-compose logs -f
docker-compose exec service /bin/sh
docker-compose build
```

### Common Flags

```bash
# docker run flags
-d              # Detached (background)
-it             # Interactive with TTY
-p 8080:80      # Port mapping (host:container)
--name myapp    # Container name
-e VAR=value    # Environment variable
-v /host:/container  # Volume mount
--network name  # Connect to network
--rm            # Remove container on exit
-m 512m         # Memory limit
--cpus 1.0      # CPU limit
```

### Dockerfile Instructions

```dockerfile
FROM image:tag           # Base image
WORKDIR /path            # Set working directory
COPY src dst             # Copy files
ADD src dst              # Copy (with URL/tar support)
RUN command              # Execute command
ENV KEY=value            # Environment variable
EXPOSE port              # Document port
CMD ["executable"]       # Default command
ENTRYPOINT ["exec"]      # Command prefix
VOLUME /path             # Create mount point
USER username            # Set user
ARG name=default         # Build argument
LABEL key=value          # Metadata
HEALTHCHECK CMD command  # Health check
```

---

## Summary

Docker containerization provides:
- **Consistency**: Identical environments from dev to production
- **Isolation**: Dependencies don't conflict between applications
- **Portability**: Run anywhere Docker runs (cloud, local, CI)
- **Efficiency**: Lightweight compared to VMs, fast startup
- **Scalability**: Easy horizontal scaling with orchestration

**Key Workflows**:
1. **Development**: docker-compose with hot reload volumes
2. **CI/CD**: Build, test, push images to registry
3. **Production**: Pull images, run with resource limits and health checks

**Next Steps**:
- Master multi-stage builds for optimal image sizes
- Implement health checks and graceful shutdown
- Set up docker-compose for local development
- Integrate Docker into CI/CD pipelines
- Explore orchestration (Swarm or Kubernetes) for production scale

© bobmatnyc · MIT · bobmatnyc/claude-mpm-skills

Similar skills

All skills for DevOps and infrastructure →

docker by other authors comes first.

docker codewithmukesh Configures Docker containerization for .NET 10 apps with multi-stage builds, non-root user setup, and orchestrator-level health probes. CX Codex +1 2,900installs docker Mindrally Establishes best practices for lean Docker images, multi-stage builds, container security, and Compose workflows. CX Codex +1 1,100installs docker-project-foundations docker This skill bootstraps Docker environments from scratch. It generates a baseline set of .dockerignore, Dockerfile, and compose.yaml files. CX Codex +1 1,000installs docker-expert PCL Team A skill that gives your coding agent Docker expertise: image builds, Dockerfile cleanups, Compose setups. Useful when a container balloons or dies on boot. CX Codex +1 365installs bun-docker secondsky A skill that hands you Dockerfile and docker-compose patterns for Bun apps: oven/bun images, multi-stage builds, hot reload in development. CX Codex +1 408installs docker-development Alireza Rezvani This skill reviews Dockerfiles, Compose files, and containers for build, image-size, and security issues. Use it when setting up or cleaning up containerized projects. 702installs docker-compose-patterns docker Rules for creating, debugging, and wiring services in compose.yaml. Configures health checks, persistent volumes, and development overrides. CX Codex +1 1,400installs docker-best-practices JosiahSiegel A Docker skill that guides your agent through Dockerfiles, base image choice and production container setup. CX Codex +1 938installs

From the same set66

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor