Tools: Docker Image Diet: Find the Problem With dive Before Trying to Fix It - Complete Guide

Tools: Docker Image Diet: Find the Problem With dive Before Trying to Fix It - Complete Guide

The Tools: docker image history + dive

Starting With a Fat Image

Step 1: docker image history

Step 2: dive to Find the Waste

Fix What's Actually Broken

Can You Go Further?

Go: A Different Story

Summary

References Your Docker image is large and you don't know why.

Guessing and applying a checklist of tips might not cut much.Find out what's actually causing the size, then fix that specifically. Two tools, different purposes. docker image history is built into Docker. It shows how much space each layer takes: dive is a third-party tool. It lets you browse each layer's contents interactively and reports how much space is being wasted: A typical Node.js Dockerfile with no optimizations: package.json has a few devDependencies: jest, typescript, @types/express. After docker build, docker images shows: 1.25GB. Don't touch the Dockerfile yet — find out where the weight is. Output (relevant lines): Three things stand out immediately: docker image history shows layer sizes. dive shows what's actually inside each layer: 107MB wasted. dive names the culprits directly: typescript, @babel/parser — all devDependencies that serve no purpose in a production image. "Count: 2" means the same file appears in two layers — once from npm install, once from COPY . .. That's what happens without a .dockerignore: node_modules gets installed, then copied in again on top. Problems identified. Fix each one: Problem 1: node_modules copied twice→ Add .dockerignore Problem 2: devDependencies in production image→ Multi-stage build, production stage uses --omit=dev Problem 3: Base image is too heavy (Debian + build tools)

→ Switch to node:20-alpine The fixed Dockerfile: Run dive again to confirm: The 139MB floor is mostly the Node.js runtime inside node:20-alpine. To go lower, switch to distroless: That gets you to around 100MB. Beyond that, the gains are small — unless you switch to Go. Go compiles to a static binary. Use scratch — a completely empty base image: Final image is just the binary. A few MB to a few dozen MB. At this point dive has little to tell you — there's almost nothing to optimize. Workflow: docker image history to find the heavy layers → dive to see what's inside them → fix the actual problem. Use tools to diagnose, make targeted fixes, then verify with tools again. More effective than guessing. Templates let you quickly answer FAQs or store snippets for re-use. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse

Command

Copy

$ -weight: 500;">docker image history <image-name> -weight: 500;">docker image history <image-name> -weight: 500;">docker image history <image-name> # Install -weight: 500;">brew -weight: 500;">install dive # macOS -weight: 500;">apt -weight: 500;">install dive # Ubuntu (requires adding repo first) # Analyze interactively dive <image-name> # CI mode (report only, no interactive UI) CI=true dive <image-name> # Install -weight: 500;">brew -weight: 500;">install dive # macOS -weight: 500;">apt -weight: 500;">install dive # Ubuntu (requires adding repo first) # Analyze interactively dive <image-name> # CI mode (report only, no interactive UI) CI=true dive <image-name> # Install -weight: 500;">brew -weight: 500;">install dive # macOS -weight: 500;">apt -weight: 500;">install dive # Ubuntu (requires adding repo first) # Analyze interactively dive <image-name> # CI mode (report only, no interactive UI) CI=true dive <image-name> FROM node:latest WORKDIR /app COPY package*.json ./ RUN -weight: 500;">npm -weight: 500;">install COPY . . CMD ["node", "index.js"] FROM node:latest WORKDIR /app COPY package*.json ./ RUN -weight: 500;">npm -weight: 500;">install COPY . . CMD ["node", "index.js"] FROM node:latest WORKDIR /app COPY package*.json ./ RUN -weight: 500;">npm -weight: 500;">install COPY . . CMD ["node", "index.js"] REPOSITORY TAG IMAGE ID SIZE demo-app latest ddb21d14ccef 1.25GB REPOSITORY TAG IMAGE ID SIZE demo-app latest ddb21d14ccef 1.25GB REPOSITORY TAG IMAGE ID SIZE demo-app latest ddb21d14ccef 1.25GB -weight: 500;">docker image history demo-app -weight: 500;">docker image history demo-app -weight: 500;">docker image history demo-app CREATED BY SIZE CMD ["node" "index.js"] 0B COPY . . 49.3MB RUN -weight: 500;">npm -weight: 500;">install 61MB COPY package*.json ./ 166kB WORKDIR /app 0B RUN ... (node binary -weight: 500;">install) 199MB RUN ... (-weight: 500;">apt-get build-essential etc.) 561MB RUN ... (-weight: 500;">apt-get base packages) 184MB # debian bookworm base 139MB CREATED BY SIZE CMD ["node" "index.js"] 0B COPY . . 49.3MB RUN -weight: 500;">npm -weight: 500;">install 61MB COPY package*.json ./ 166kB WORKDIR /app 0B RUN ... (node binary -weight: 500;">install) 199MB RUN ... (-weight: 500;">apt-get build-essential etc.) 561MB RUN ... (-weight: 500;">apt-get base packages) 184MB # debian bookworm base 139MB CREATED BY SIZE CMD ["node" "index.js"] 0B COPY . . 49.3MB RUN -weight: 500;">npm -weight: 500;">install 61MB COPY package*.json ./ 166kB WORKDIR /app 0B RUN ... (node binary -weight: 500;">install) 199MB RUN ... (-weight: 500;">apt-get build-essential etc.) 561MB RUN ... (-weight: 500;">apt-get base packages) 184MB # debian bookworm base 139MB CI=true dive demo-app CI=true dive demo-app CI=true dive demo-app efficiency: 95.49 % wastedBytes: 107 MB userWastedPercent: 9.68 % Inefficient Files: Count Wasted Space File Path 2 18 MB /app/node_modules/typescript/lib/typescript.js 2 12 MB /app/node_modules/typescript/lib/_tsc.js 2 3.7 MB /app/node_modules/typescript/lib/lib.dom.d.ts 2 2.9 MB /app/node_modules/@babel/parser/lib/index.js.map ... efficiency: 95.49 % wastedBytes: 107 MB userWastedPercent: 9.68 % Inefficient Files: Count Wasted Space File Path 2 18 MB /app/node_modules/typescript/lib/typescript.js 2 12 MB /app/node_modules/typescript/lib/_tsc.js 2 3.7 MB /app/node_modules/typescript/lib/lib.dom.d.ts 2 2.9 MB /app/node_modules/@babel/parser/lib/index.js.map ... efficiency: 95.49 % wastedBytes: 107 MB userWastedPercent: 9.68 % Inefficient Files: Count Wasted Space File Path 2 18 MB /app/node_modules/typescript/lib/typescript.js 2 12 MB /app/node_modules/typescript/lib/_tsc.js 2 3.7 MB /app/node_modules/typescript/lib/lib.dom.d.ts 2 2.9 MB /app/node_modules/@babel/parser/lib/index.js.map ... node_modules .-weight: 500;">git .env *.log node_modules .-weight: 500;">git .env *.log node_modules .-weight: 500;">git .env *.log # Stage 1: -weight: 500;">install everything (including devDeps for build) FROM node:20-alpine AS builder WORKDIR /app COPY package*.json ./ RUN -weight: 500;">npm ci COPY . . # If you have TypeScript: RUN -weight: 500;">npm run build # Stage 2: production dependencies only FROM node:20-alpine AS production WORKDIR /app COPY package*.json ./ RUN -weight: 500;">npm ci --omit=dev # no jest, no typescript COPY --from=builder /app/index.js ./ CMD ["node", "index.js"] # Stage 1: -weight: 500;">install everything (including devDeps for build) FROM node:20-alpine AS builder WORKDIR /app COPY package*.json ./ RUN -weight: 500;">npm ci COPY . . # If you have TypeScript: RUN -weight: 500;">npm run build # Stage 2: production dependencies only FROM node:20-alpine AS production WORKDIR /app COPY package*.json ./ RUN -weight: 500;">npm ci --omit=dev # no jest, no typescript COPY --from=builder /app/index.js ./ CMD ["node", "index.js"] # Stage 1: -weight: 500;">install everything (including devDeps for build) FROM node:20-alpine AS builder WORKDIR /app COPY package*.json ./ RUN -weight: 500;">npm ci COPY . . # If you have TypeScript: RUN -weight: 500;">npm run build # Stage 2: production dependencies only FROM node:20-alpine AS production WORKDIR /app COPY package*.json ./ RUN -weight: 500;">npm ci --omit=dev # no jest, no typescript COPY --from=builder /app/index.js ./ CMD ["node", "index.js"] -weight: 500;">docker build -t demo-app-fixed . -weight: 500;">docker images | grep demo -weight: 500;">docker build -t demo-app-fixed . -weight: 500;">docker images | grep demo -weight: 500;">docker build -t demo-app-fixed . -weight: 500;">docker images | grep demo REPOSITORY SIZE demo-app-fixed 139MB ← down from 1.25GB demo-app 1.25GB REPOSITORY SIZE demo-app-fixed 139MB ← down from 1.25GB demo-app 1.25GB REPOSITORY SIZE demo-app-fixed 139MB ← down from 1.25GB demo-app 1.25GB efficiency: 99.96 % wastedBytes: 75 kB ← down from 107MB efficiency: 99.96 % wastedBytes: 75 kB ← down from 107MB efficiency: 99.96 % wastedBytes: 75 kB ← down from 107MB FROM gcr.io/distroless/nodejs20-debian12 AS production WORKDIR /app COPY --from=builder /app/node_modules ./node_modules COPY --from=builder /app/index.js ./ CMD ["index.js"] FROM gcr.io/distroless/nodejs20-debian12 AS production WORKDIR /app COPY --from=builder /app/node_modules ./node_modules COPY --from=builder /app/index.js ./ CMD ["index.js"] FROM gcr.io/distroless/nodejs20-debian12 AS production WORKDIR /app COPY --from=builder /app/node_modules ./node_modules COPY --from=builder /app/index.js ./ CMD ["index.js"] FROM golang:1.22-alpine AS builder WORKDIR /app COPY . . RUN CGO_ENABLED=0 go build -o server . FROM scratch COPY --from=builder /app/server /server ENTRYPOINT ["/server"] FROM golang:1.22-alpine AS builder WORKDIR /app COPY . . RUN CGO_ENABLED=0 go build -o server . FROM scratch COPY --from=builder /app/server /server ENTRYPOINT ["/server"] FROM golang:1.22-alpine AS builder WORKDIR /app COPY . . RUN CGO_ENABLED=0 go build -o server . FROM scratch COPY --from=builder /app/server /server ENTRYPOINT ["/server"] - 561MB -weight: 500;">apt-get layer: build-essential, python3, gcc — build tools that aren't needed at runtime, but they're stuck in the image - 61MB -weight: 500;">npm -weight: 500;">install: includes devDependencies (jest, typescript) that production doesn't use - 49.3MB COPY . .: node_modules got copied in (no .dockerignore) - Bloated base image: node:latest (Debian) → node:20-alpine - devDependencies in production: multi-stage build + --omit=dev - Duplicate node_modules: add .dockerignore - dive on GitHub — Docker image layer explorer - Docker Docs — Multi-stage builds - Docker Docs — .dockerignore file - GoogleContainerTools Distroless images - Docker Docs — -weight: 500;">docker image history