Dockerfile and Compose Review: Size, Cache, Security

Rewrites Dockerfiles and compose files for smaller images, cache-friendly builds, and non-root, secret-free containers, with reasons.

by AgentVoltv1.0.0
Free to sign up · every skill included with AgentVolt Pro

Before & after

Same prompt, same agent — the only difference is the skill.

Prompt“Optimize our production Dockerfile for size and security”
Beforegeneric AI agent
Optimizing Dockerfiles involves a few techniques. Multi-stage builds can help reduce image size by splitting build and runtime layers. Security-wise, you'd want to think about what dependencies are actually needed and avoid running as root. Do you have a current Dockerfile you want to improve? What base image are you using? Are you deploying to Kubernetes or traditional VMs? The specifics matter for what optimizations make sense.
Afterwith Dockerfile and Compose Review: Size, Cache, Security
PRODUCTION DOCKERFILE: OPTIMIZED MULTI-STAGE BUILD

→ STAGE 1: BUILDER (dependencies + compilation)
  FROM node:20-alpine AS builder
  WORKDIR /app
  COPY package*.json ./
  RUN npm ci --only=production \  # Only prod deps (excludes dev tooling)
    && npm cache clean --force
  COPY . .
  RUN npm run build  # Compile TypeScript/assets
  
  Size after stage 1: 450MB (includes build tools, temp files – doesn't matter)
  Trap: Avoid RUN npm install (reinstalls dev deps); use npm ci for locked versions

→ STAGE 2: RUNTIME (final production image)
  FROM node:20-alpine  # Slim base (140MB vs 900MB with ubuntu)
  
  # Security: Create non-root user
  RUN addgroup -g 1001 -S nodejs
  RUN adduser -S nodejs -u 1001
  
  WORKDIR /app
  COPY --from=builder --chown=nodejs:nodejs /app/node_modules ./node_modules
  COPY --from=builder --chown=nodejs:nodejs /app/dist ./dist
  COPY --from=builder --chown=nodejs:nodejs /app/package*.json ./
  
  USER nodejs  # Switch to non-root (prevents container breakout)
  EXPOSE 3000
  CMD ["node", "dist/server.js"]
  
  Final size: ~220MB (node_modules + dist + base image)
  Reduction: 75% smaller than single-stage (900MB → 220MB)

→ LAYER CACHING STRATEGY (speeds up rebuilds)
  Order matters: Copy package.json BEFORE source code
  Logic: package changes infrequently; source changes often
  If source changes: Skip npm ci rebuild (cache hit)
  If package changes: Only rebuild node_modules (cache miss on layer 3)
  Example rebuild time: 45s with cache vs. 4min without

→ SECURITY HARDENING
  ✓ Non-root user (USER nodejs, uid 1001 – prevents root exploits)
  ✓ Alpine base (smaller attack surface, fewer packages)
  ✓ No secrets in Dockerfile (use Docker secrets or env vars at runtime)
  ✓ Scan image: docker scout cves (catches known vulnerabilities in base image)
  ✓ Read-only root: Add --read-only flag in orchestrator (Kubernetes, etc.)
  ✓ Pin base image version: node:20.8-alpine not node:20-alpine (fixed point-in-time)
  Trap: Running as root in production is #1 vulnerability – don't skip USER line

→ RUNTIME OPTIMIZATIONS
  Environment: NODE_ENV=production (skips dev dependencies in app startup)
  Memory: Set JVM/Node heap if needed (--memory=512m in docker run)
  Health check: Add HEALTHCHECK instruction (orchestrator knows container status)

→ DEPLOYMENT CHECKLIST
  ✓ Build locally: docker build -t myapp:latest .
  ✓ Test: docker run -u 1001 myapp:latest (verify non-root works)
  ✓ Scan: docker scout cves myapp:latest (zero critical vulns)
  ✓ Size check: docker images | grep myapp (should be <300MB)
  ✓ Push to registry: docker push myregistry.com/myapp:v1.0.0
  ✓ Verify in prod: k logs pod/myapp (confirm app starts, no permission errors)

About this skill

Your image is 2 GB, every docker build reinstalls all dependencies after a one-line code change, the container takes 10 seconds to stop, and you are not sure whether that ARG NPM_TOKEN ended up in the image. This skill reads your Dockerfile and compose file the way an experienced platform engineer would. It returns a corrected version with every change explained, and it tells you how to confirm each fix on your own machine.

What it does

  • Reorders layers and adds BuildKit cache mounts so code edits stop triggering full dependency reinstalls
  • Converts single-stage builds to multi-stage builds and picks a base image that fits your language (slim, distroless, or scratch), including the musl and native-module traps
  • Finds secrets that leak through ARG, ENV, COPY-then-delete, or a missing .dockerignore, and moves them to build secret mounts or runtime injection
  • Hardens the runtime: numeric non-root user, exec-form CMD and correct PID 1 signal handling, dropped capabilities, read-only filesystem
  • Fixes compose problems: healthcheck-gated startup, node_modules hidden by bind mounts, dev and prod split, and published database ports that bypass the host firewall
  • Ranks findings by impact and gives a verification command for each one (docker history, docker inspect, image size comparisons)

What you get

A prioritized findings list, the rewritten Dockerfile, .dockerignore, and compose file, and a short checklist of commands to prove the improvements yourself.

Best for

Backend and full-stack developers who containerize their own apps, small teams moving a compose setup toward production, and anyone reviewing a Dockerfile in a PR.

SKILL.md preview

SKILL.md
---
name: dockerfile-compose-review
description: Reviews and rewrites Dockerfiles and docker-compose files for smaller images, cache-efficient BuildKit builds, and a hardened runtime (numeric non-root user, no baked secrets, correct PID 1 signal handling), ranking findings by impact and giving a verification command for each. Use when the user says "optimize my Dockerfile", "my image is 2GB", "docker build reinstalls everything every time I change code", "convert this to a multi-stage build", "is this Dockerfile secure", "my container takes 10 seconds to stop", "review my docker-compose", or "my app starts before the database is ready".
---

… (sign up to view the full skill)
Sign up to view, copy, and install the full skill

More development skills

View all Development skills →