AI Tools Compared

Docker image bloat is a recurring problem: images that start at 200MB balloon to 1.8GB over months as teams add dependencies without removing base layers. AI tools accelerate the optimization cycle by identifying inefficiencies, rewriting Dockerfiles with correct layer ordering, and suggesting base image alternatives.

Prerequisites

Before you begin, make sure you have the following ready:

Step 1: Diagnosing Bloat Before AI Assistance

Before asking AI to optimize a Dockerfile, gather the data it needs:

# Show layer sizes
docker history --human --format "{{.Size}}\t{{.CreatedBy}}" my-app:latest | sort -rh | head -20

# Show total image size
docker images my-app:latest

# Dive: interactive layer explorer
brew install dive
dive my-app:latest

Feed the docker history output to Claude with the Dockerfile. This gives the model concrete evidence of where size is coming from, not just the instructions.

Step 2: Prompt Pattern for Dockerfile Optimization

Here is my Dockerfile and the layer sizes from `docker history`.
Optimize it for:
1. Smallest possible final image size
2. Best cache hit rate for typical development changes
3. Non-root user for security
4. Correct layer ordering (dependencies before source code)

[paste Dockerfile]
[paste docker history output]

Step 3: Node.js: Before and After

Before (1.2GB image):

FROM node:20
WORKDIR /app
COPY . .
RUN npm install
RUN npm run build
EXPOSE 3000
CMD ["node", "dist/index.js"]

Problems: copies all source before installing deps (breaks cache on every file change), uses full Node image (includes build tools), no .dockerignore.

After (Claude’s rewrite, 187MB):

# Build stage
FROM node:20-alpine AS builder
WORKDIR /app

# Dependencies first — cached until package*.json changes
COPY package.json package-lock.json ./
RUN npm ci --only=production=false

# Source code (cache misses here don't invalidate deps layer)
COPY tsconfig.json ./
COPY src/ ./src/
RUN npm run build

# Prune dev dependencies
RUN npm prune --production

# Runtime stage — no build tools
FROM node:20-alpine AS runtime
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
WORKDIR /app

COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package.json ./

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

.dockerignore (Claude generates this too):

node_modules
dist
.git
.gitignore
*.md
.env
.env.*
coverage
.nyc_output

Size reduction: 1.2GB → 187MB. Cache hit rate improves because package.json changes far less often than source files.

Step 4: Python: Multi-Stage with uv

Before (890MB):

FROM python:3.12
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0"]

After (Claude suggests using uv and slim base, 95MB):

# Build stage: install deps with uv (much faster than pip)
FROM python:3.12-slim AS builder
RUN pip install uv

WORKDIR /app
COPY pyproject.toml uv.lock ./
RUN uv sync --frozen --no-dev --no-editable

# Runtime stage
FROM python:3.12-slim AS runtime

# Security: non-root user
RUN groupadd --gid 1001 appgroup && \
    useradd --uid 1001 --gid appgroup --shell /bin/bash --create-home appuser

WORKDIR /app

# Copy only the virtual environment, not build tools
COPY --from=builder /app/.venv /app/.venv
COPY --chown=appuser:appgroup src/ ./src/

ENV PATH="/app/.venv/bin:$PATH"
USER appuser

EXPOSE 8000
CMD ["uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "8000"]

The uv sync --frozen guarantees reproducibility. The separate build/runtime stages eliminate uv itself from the final image.

Step 5: Go: Static Binary

Go produces statically compiled binaries, enabling the smallest possible images:

# Build stage: full Go toolchain
FROM golang:1.23-alpine AS builder

WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download

COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o server ./cmd/server

# Runtime: scratch image (no OS at all)
FROM scratch AS runtime
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=builder /app/server /server

EXPOSE 8080
ENTRYPOINT ["/server"]

The -ldflags="-s -w" strips debug symbols and DWARF info, reducing binary size by 30-40%. The scratch base image adds zero overhead — the final image is just the binary plus TLS certs.

Size comparison:

Image Size
golang:1.23 (full) 1.2GB
golang:1.23-alpine 260MB
gcr.io/distroless/static 5MB + binary
scratch 0MB + binary

Step 6: Use Claude to Audit Existing Dockerfiles

Ask Claude to audit without rewriting — useful when you need to understand tradeoffs before committing to changes:

Audit this Dockerfile for: security issues, layer caching problems,
unnecessary size, and any deprecated practices. List findings as
a numbered list with severity (High/Medium/Low) and the specific
line number. Do not rewrite the file.

Common findings Claude surfaces that humans miss:

Correct apt pattern:

RUN apt-get update && \
    apt-get install -y --no-install-recommends \
        curl \
        git && \
    rm -rf /var/lib/apt/lists/*

Step 7: Layer Cache Analysis Workflow

# Build with BuildKit and capture provenance
DOCKER_BUILDKIT=1 docker build --progress=plain -t my-app:test . 2>&1 | tee build.log

# Ask Claude to analyze the build log
# Paste build.log content and ask:
# "Which layers are cache misses? What's causing them?
#  How should I reorder the Dockerfile to maximize cache hits?"

Step 8: Scanning for Vulnerabilities

After optimizing, scan the final image:

# Trivy scan
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
  aquasec/trivy:latest image my-app:latest

# Or with Docker Scout
docker scout cves my-app:latest

Feed Trivy output to Claude to prioritize fixes:

Here is my Trivy scan output. Which CVEs should I fix first?
I'm running a public-facing API. Focus on network-exploitable
vulnerabilities in my base image. Suggest specific base image
versions that eliminate the HIGH/CRITICAL findings.

Step 9: Automate Optimization Checks in CI

# .github/workflows/docker-check.yml
name: Docker Image Check
on: [push]
jobs:
  size-check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Build image
        run: docker build -t app:test .
      - name: Check image size
        run: |
          SIZE=$(docker image inspect app:test --format='{{.Size}}')
          MAX_BYTES=524288000  # 500MB limit
          if [ "$SIZE" -gt "$MAX_BYTES" ]; then
            echo "Image size ${SIZE} bytes exceeds limit ${MAX_BYTES} bytes"
            exit 1
          fi
          echo "Image size: $(docker image inspect app:test --format='{{.Size}}') bytes - OK"
      - name: Scan for vulnerabilities
        run: docker run --rm -v /var/run/docker.sock:/var/run/docker.sock aquasec/trivy:latest image --exit-code 1 --severity HIGH,CRITICAL app:test

Troubleshooting

Configuration changes not taking effect

Restart the relevant service or application after making changes. Some settings require a full system reboot. Verify the configuration file path is correct and the syntax is valid.

Permission denied errors

Run the command with sudo for system-level operations, or check that your user account has the necessary permissions. On macOS, you may need to grant terminal access in System Settings > Privacy & Security.

Connection or network-related failures

Check your internet connection and firewall settings. If using a VPN, try disconnecting temporarily to isolate the issue. Verify that the target server or service is accessible from your network.

Frequently Asked Questions

How long does it take to use ai for docker image optimization?

For a straightforward setup, expect 30 minutes to 2 hours depending on your familiarity with the tools involved. Complex configurations with custom requirements may take longer. Having your credentials and environment ready before starting saves significant time.

What are the most common mistakes to avoid?

The most frequent issues are skipping prerequisite steps, using outdated package versions, and not reading error messages carefully. Follow the steps in order, verify each one works before moving on, and check the official documentation if something behaves unexpectedly.

Do I need prior experience to follow this guide?

Basic familiarity with the relevant tools and command line is helpful but not strictly required. Each step is explained with context. If you get stuck, the official documentation for each tool covers fundamentals that may fill in knowledge gaps.

Can I adapt this for a different tech stack?

Yes, the underlying concepts transfer to other stacks, though the specific implementation details will differ. Look for equivalent libraries and patterns in your target stack. The architecture and workflow design remain similar even when the syntax changes.

Where can I get help if I run into issues?

Start with the official documentation for each tool mentioned. Stack Overflow and GitHub Issues are good next steps for specific error messages. Community forums and Discord servers for the relevant tools often have active members who can help with setup problems.