Claude Code for Docker — Dockerfile, Compose & Container Workflows

Docker is the universal packaging layer for every language and cloud. This guide shows how to configure Claude Code to generate production-grade Dockerfiles, multi-stage builds, and docker-compose stacks — and how to debug containers without leaving your terminal.

CLAUDE.md Template for Containerized Projects

Tell Claude Code your container constraints up front so every generated Dockerfile follows your standards automatically.

# CLAUDE.md — Containerized Service

## Build commands
- docker build -t myapp:dev .
- docker compose up -d
- docker compose logs -f app
- docker compose down -v  # full teardown including volumes

## Container requirements
- Base image: node:22-alpine (or python:3.12-slim for Python services)
- Multi-stage build required for all production Dockerfiles
- Final stage must run as non-root (USER node or USER appuser)
- No secrets in Dockerfile — use runtime env vars or secrets mounts
- .dockerignore must exclude: node_modules, .git, .env, dist, __pycache__

## Compose topology
- Services: app (port 3000), postgres:16-alpine, redis:7-alpine
- Custom bridge network: app_network
- Named volumes for postgres data: pg_data
- Health checks required on postgres and redis

## Image size targets
- Development image: < 500 MB acceptable
- Production image: < 150 MB target (use multi-stage, alpine base)

Generating a Production Dockerfile (Multi-Stage)

Prompt Claude Code to generate a complete multi-stage Dockerfile for your service. The more context in CLAUDE.md, the fewer iterations needed.

# Example prompt
"Generate a multi-stage Dockerfile for this Node.js API.
Stage 1 (builder): install all deps, build TypeScript.
Stage 2 (production): copy only dist + node_modules/prod,
run as non-root user 'node', expose port 3000, health check
GET /health every 30s. Use node:22-alpine throughout."
# Claude produces — production Node.js Dockerfile
FROM node:22-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:22-alpine AS production
WORKDIR /app
RUN addgroup -S appgroup && adduser -S node -G appgroup 2>/dev/null || true
COPY --from=builder --chown=node:node /app/dist ./dist
COPY --from=builder --chown=node:node /app/node_modules ./node_modules
COPY --from=builder --chown=node:node /app/package.json ./package.json
USER node
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
  CMD wget -qO- http://localhost:3000/health || exit 1
CMD ["node", "dist/server.js"]
Add a .dockerignore to every project. Without it, every COPY . . instruction sends your entire repo context to the daemon. Ask Claude: "Generate a .dockerignore for this Node.js + TypeScript project."

docker-compose.yml for Local Development

# Prompt
"Generate a docker-compose.yml for local dev. Services:
app (build from .), postgres:16-alpine with named volume,
redis:7-alpine. App depends on postgres/redis being healthy.
Mount src/ for hot-reload. Expose app on 3000, postgres on 5432."
# Claude produces
services:
  app:
    build: .
    ports: ["3000:3000"]
    volumes:
      - ./src:/app/src:ro
    environment:
      DATABASE_URL: postgres://dev:dev@postgres:5432/myapp
      REDIS_URL: redis://redis:6379
    depends_on:
      postgres: {condition: service_healthy}
      redis:    {condition: service_healthy}
    networks: [app_network]

  postgres:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: myapp
      POSTGRES_USER: dev
      POSTGRES_PASSWORD: dev
    volumes: [pg_data:/var/lib/postgresql/data]
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U dev -d myapp"]
      interval: 5s
      timeout: 5s
      retries: 10
    networks: [app_network]

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

volumes:
  pg_data:

networks:
  app_network:
    driver: bridge

Hooks for Docker Validation

Catch Dockerfile and compose errors immediately after every edit, not at deploy time.

# .claude/settings.json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "if [ -f Dockerfile ]; then docker build --check . 2>&1 | head -30; fi"
          },
          {
            "type": "command",
            "command": "if [ -f docker-compose.yml ]; then docker compose config --quiet 2>&1 | head -20; fi"
          }
        ]
      }
    ]
  }
}
docker build --check . (Docker Desktop 4.27+ / Engine 25+) validates Dockerfile syntax and best practices without building the image — fast feedback for every edit.

Docker Workflow Prompt Patterns

GoalPrompt to Claude Code
Reduce image size"Audit this Dockerfile. Identify unused layers, dev dependencies in production, and cache-busting COPY order. Suggest optimizations with expected size reduction."
Add Docker secrets"Refactor the Dockerfile to use Docker BuildKit secrets for npm install instead of passing the NPM_TOKEN as a build arg."
Multi-arch build"Add a GitHub Actions workflow that builds and pushes amd64 + arm64 images to Docker Hub using docker buildx bake."
Distroless base"Migrate the production stage from node:22-alpine to gcr.io/distroless/nodejs22-debian12. Show what changes are needed."
Compose override"Generate docker-compose.override.yml for local dev with volumes for hot-reload and DEBUG=true, leaving compose.yml unchanged for CI."
Kubernetes migration"Convert this docker-compose.yml to Kubernetes Deployment + Service + ConfigMap manifests."

Debugging Container Issues

Paste container output directly into Claude Code — it identifies the failure mode and proposes a fix in one shot.

CrashLoopBackOff / OOMKilled

# Run in terminal, paste output to Claude
docker logs --tail 50 $(docker ps -lq)
docker inspect $(docker ps -lq) | jq '.[0].State'

# Claude Code prompt
"Container exits with code 137 (OOMKilled). Here are the logs: [paste].
Recommend memory limit tuning and profiling approach."

Port Conflicts

# Diagnose bind errors
docker compose up 2>&1 | grep -E "bind|address already in use"

# Claude Code prompt
"Port 5432 bind failed. Identify which local process holds it
and suggest compose port mapping fix."

Volume Permission Errors

# Claude Code prompt
"Container writes fail with EACCES on /data. The volume is mounted
from host. Show how to fix ownership with initContainers or entrypoint chown."

Layer Caching Best Practices

PatternWhy it mattersExample
COPY package.json before sourceInvalidates dep layer only when deps change, not on every source editCOPY package*.json ./ && RUN npm ci
Pin base image digests in CIReproducible builds; SHA prevents silent upstream changesFROM node:22-alpine@sha256:abc...
Combine RUN commandsEach RUN is a layer; combining reduces final image sizeRUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
Use BuildKit cache mountsPersists package manager caches across builds without layer bloatRUN --mount=type=cache,target=/root/.npm npm ci
Multi-stage for test isolationTest stage includes dev deps; production stage copies only built artifactsThree stages: base → test → production
Estimate Claude API costs for your containerized services
Try the Claude Cost Calculator →

Python Service Dockerfile Pattern

# Claude Code prompt for Python services
"Generate a multi-stage Dockerfile for a FastAPI service:
builder installs dependencies with pip, production uses
python:3.12-slim, runs uvicorn on port 8000 as non-root,
includes healthcheck on /health. Use .venv pattern for isolation."
FROM python:3.12-slim AS builder
WORKDIR /app
RUN python -m venv /app/.venv
ENV PATH="/app/.venv/bin:$PATH"
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

FROM python:3.12-slim AS production
WORKDIR /app
RUN useradd -r -s /bin/false appuser
COPY --from=builder --chown=appuser /app/.venv /app/.venv
COPY --chown=appuser . .
ENV PATH="/app/.venv/bin:$PATH"
USER appuser
EXPOSE 8000
HEALTHCHECK --interval=30s --timeout=5s \
  CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')"
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]

5 Docker Tips for Claude Code Users

Related Guides

Kubernetes workflows →  |  Terraform & IaC →  |  CI/CD automation →  |  Python & FastAPI →  |  Hook patterns →

📘 Free: 5 sample Claude Code prompts · plus the £3 pack with 25 moreSee the free 5 →