What it does
Optimizes Dockerfiles for smaller images, faster builds, better caching, and security hardening using multi-stage builds and best practices
Skills ZICQ category:Security dockerfile-optimizer
Optimizes Dockerfiles for smaller images, faster builds, better caching, and security hardening using multi-stage builds and best practices. Use when users request "optimize Dockerfile", "reduce Docker image size", "Docker best practices", or "containerize application".
Official URL:skills.sh
Intro in this page language first. The official description stays in its original wording; we do not rewrite SKILL.md.
Optimizes Dockerfiles for smaller images, faster builds, better caching, and security hardening using multi-stage builds and best practices
users request "optimize Dockerfile", "reduce Docker image size", "Docker best practices", or "containerize application"
Per Agent Skills progressive disclosure: name and description load at startup (~100 tokens); the full SKILL.md body loads when the skill activates; scripts/, references/, and assets/ load only as needed. This file's sections: Dockerfile Optimizer; Core Workflow; Base Image Selection; Image Size Comparison; Recommendations by Language; Node.js. It includes spec-recommended sections: step-by-step instructions.
File analysis: instruction-only skill (SKILL.md). The agent loads the full body when activated. The body is about 626 lines, above the spec’s 500-line guidance; agents still load it in full on activation.
Dockerfile OptimizerCore WorkflowBase Image SelectionImage Size ComparisonRecommendations by LanguageNode.jsPythonGoRustJavaMulti-Stage BuildsNode.js Application
Source category:skills.sh agent-skill
namedockerfile-optimizerdescriptionInvocation syntax and available tools depend on your Agent client. Client integration guide ↗
Choose the target agent and installation scope, keep referenced package files, then verify the skill appears in the client's catalog.
Copy these instructions to a compatible agent and confirm the target directory matches your client.
Install the agent skill "dockerfile-optimizer" into my project. The full SKILL.md and official description are at https://zicq.com/en/skills/skl-7b150a5279ccfc11-Dockerfile-Optimizer.html Save it as .cursor/skills/dockerfile-optimizer/SKILL.md or .claude/skills/dockerfile-optimizer/SKILL.md and keep the frontmatter name and description exactly as-is.
Requires Node.js and npx. First inspect the repository's skill list to confirm the name.
npx skills add 'https://github.com/patricio0312rev/skills' --list
npx skills add 'https://github.com/patricio0312rev/skills' --skill 'dockerfile-optimizer'
The CLI lets you choose the agent interactively. The default scope is the project; use -g for user scope. Confirm package availability with the discovery command, then use npx skills list to inspect installed skills.
Build optimized, secure, and cache-efficient Docker images following production best practices.
| Base Image | Size | Use Case |
|------------|------|----------|
| node:20 | ~1GB | Development only |
| node:20-slim | ~200MB | General production |
| node:20-alpine | ~130MB | Size-critical production |
| gcr.io/distroless/nodejs20 | ~120MB | Maximum security |
# Node.js
FROM node:20-alpine
# Python
FROM python:3.12-slim
# Go
FROM golang:1.22-alpine AS builder
FROM scratch AS runtime # Or gcr.io/distroless/static
# Rust
FROM rust:1.75-alpine AS builder
FROM alpine:3.19 AS runtime
# Java
FROM eclipse-temurin:21-jdk-alpine AS builder
FROM eclipse-temurin:21-jre-alpine AS runtime
# ==================== Build Stage ====================
FROM node:20-alpine AS builder
WORKDIR /app
# Install dependencies first (cache layer)
COPY package.json package-lock.json ./
RUN npm ci --ignore-scripts
# Copy source and build
COPY . .
RUN npm run build
# Prune dev dependencies
RUN npm prune --production
# ==================== Production Stage ====================
FROM node:20-alpine AS production
# Security: Create non-root user
RUN addgroup -g 1001 -S nodejs && \
adduser -S nextjs -u 1001
WORKDIR /app
# Copy only necessary files
COPY --from=builder --chown=nextjs:nodejs /app/node_modules ./node_modules
COPY --from=builder --chown=nextjs:nodejs /app/dist ./dist
COPY --from=builder --chown=nextjs:nodejs /app/package.json ./
# Security: Switch to non-root user
USER nextjs
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD node -e "require('http').get('http://localhost:3000/health', (r) => process.exit(r.statusCode === 200 ? 0 : 1))"
EXPOSE 3000
CMD ["node", "dist/index.js"]
# ==================== Dependencies ====================
FROM node:20-alpine AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
# ==================== Builder ====================
FROM node:20-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
# Disable telemetry during build
ENV NEXT_TELEMETRY_DISABLED=1
RUN npm run build
# ==================== Runner ====================
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
RUN addgroup --system --gid 1001 nodejs && \
adduser --system --uid 1001 nextjs
# Copy static assets
COPY --from=builder /app/public ./public
# Set correct permissions for prerender cache
RUN mkdir .next && chown nextjs:nodejs .next
# Copy build output
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
HEALTHCHECK --interval=30s --timeout=3s \
CMD wget --no-verbose --tries=1 --spider http://localhost:3000/api/health || exit 1
CMD ["node", "server.js"]
# ==================== Builder ====================
FROM python:3.12-slim AS builder
WORKDIR /app
# Install build dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
&& rm -rf /var/lib/apt/lists/*
# Create virtual environment
RUN python -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
# Install dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# ==================== Production ====================
FROM python:3.12-slim AS production
WORKDIR /app
# Create non-root user
RUN groupadd -r appuser && useradd -r -g appuser appuser
# Copy virtual environment from builder
COPY --from=builder /opt/venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
# Copy application code
COPY --chown=appuser:appuser . .
USER appuser
EXPOSE 8000
HEALTHCHECK --interval=30s --timeout=3s \
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"]
# ==================== Builder ====================
FROM golang:1.22-alpine AS builder
RUN apk add --no-cache git ca-certificates tzdata
WORKDIR /app
# Download dependencies
COPY go.mod go.sum ./
RUN go mod download && go mod verify
# Build
COPY . .
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \
-ldflags="-w -s -X main.version=$(git describe --tags --always)" \
-o /app/server ./cmd/server
# ==================== Production ====================
FROM scratch AS production
# Copy CA certificates for HTTPS
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo
# Copy binary
COPY --from=builder /app/server /server
EXPOSE 8080
ENTRYPOINT ["/server"]
# ✓ GOOD: Least changing → Most changing
FROM node:20-alpine
# 1. System dependencies (rarely change)
RUN apk add --no-cache dumb-init
# 2. Create user (rarely changes)
RUN adduser -D appuser
# 3. Set working directory
WORKDIR /app
# 4. Copy dependency files (change occasionally)
COPY package.json package-lock.json ./
# 5. Install dependencies (cached if package files unchanged)
RUN npm ci --production
# 6. Copy source code (changes frequently)
COPY --chown=appuser:appuser . .
USER appuser
CMD ["dumb-init", "node", "index.js"]
# ✗ BAD: Source code before dependencies
FROM node:20-alpine
WORKDIR /app
COPY . . # Invalidates cache on ANY file change
RUN npm install # Must reinstall every time
CMD ["node", "index.js"]
# Version control
.git
.gitignore
# Dependencies (reinstalled in container)
node_modules
.pnpm-store
# Build outputs
dist
build
.next
out
# Development files
.env*.local
*.log
coverage
.nyc_output
# IDE
.idea
.vscode
*.swp
*.swo
# Docker
Dockerfile*
docker-compose*
.docker
# Documentation
*.md
docs
# Tests (unless needed in container)
__tests__
*.test.ts
*.spec.ts
jest.config.*
# ✓ GOOD: Install and clean in one layer
RUN apt-get update && \
apt-get install -y --no-install-recommends \
curl \
ca-certificates \
&& rm -rf /var/lib/apt/lists/* \
&& apt-get clean
# ✗ BAD: Separate layers (cleanup doesn't reduce size)
RUN apt-get update
RUN apt-get install -y curl
RUN rm -rf /var/lib/apt/lists/* # Too late, already in previous layer
# ✓ Minimal installation
RUN apt-get install -y --no-install-recommends package-name
# ✗ Installs unnecessary recommended packages
RUN apt-get install -y package-name
# Alpine uses apk, not apt
RUN apk add --no-cache \
curl \
git \
&& rm -rf /var/cache/apk/*
# Create user in Debian/Ubuntu
RUN groupadd -r appgroup && useradd -r -g appgroup appuser
# Create user in Alpine
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
# Set ownership and switch user
COPY --chown=appuser:appgroup . .
USER appuser
# In docker-compose.yml or docker run
services:
app:
read_only: true
tmpfs:
- /tmp
- /var/run
# Add labels for security scanning
LABEL org.opencontainers.image.source="https://github.com/org/repo"
LABEL org.opencontainers.image.description="Application description"
LABEL org.opencontainers.image.licenses="MIT"
# docker-compose.yml
services:
app:
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE # Only if binding to port < 1024
security_opt:
- no-new-privileges:true
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1
# Node.js
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD node -e "require('http').get('http://localhost:3000/health', (r) => process.exit(r.statusCode === 200 ? 0 : 1))"
# Python
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')" || exit 1
# wget (Alpine)
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1
# Build-time arguments
ARG NODE_ENV=production
ARG APP_VERSION=unknown
# Runtime environment variables
ENV NODE_ENV=$NODE_ENV
ENV APP_VERSION=$APP_VERSION
# Don't include secrets in Dockerfile
# Use docker run --env-file or secrets management
# docker-compose.yml
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile
target: development # Multi-stage target
volumes:
- .:/app
- /app/node_modules # Anonymous volume for node_modules
ports:
- "3000:3000"
environment:
- NODE_ENV=development
command: npm run dev
app-prod:
build:
context: .
dockerfile: Dockerfile
target: production
ports:
- "3000:3000"
environment:
- NODE_ENV=production
healthcheck:
test: ["CMD", "wget", "--spider", "http://localhost:3000/health"]
interval: 30s
timeout: 3s
retries: 3
# .github/workflows/docker.yml
name: Docker Build
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
tags: |
ghcr.io/${{ github.repository }}:latest
ghcr.io/${{ github.repository }}:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Scan for vulnerabilities
uses: aquasecurity/trivy-action@master
with:
image-ref: ghcr.io/${{ github.repository }}:${{ github.sha }}
format: 'table'
exit-code: '1'
severity: 'CRITICAL,HIGH'
# ✓ Pinned versions for reproducibility
FROM node:20.11.0-alpine3.19
# ✗ Latest tags can break builds
FROM node:latest
FROM node:20-alpine
# ✓ COPY is explicit and preferred
COPY package.json .
# ✗ ADD has extra features rarely needed
ADD package.json . # Only use for URLs or tar extraction
# ✓ Single layer, smaller image
RUN apt-get update && \
apt-get install -y package1 package2 && \
rm -rf /var/lib/apt/lists/*
# ✗ Multiple layers, larger image
RUN apt-get update
RUN apt-get install -y package1
RUN apt-get install -y package2
# View layer history
docker history image-name
# Analyze image with dive
docker run --rm -it \
-v /var/run/docker.sock:/var/run/docker.sock \
wagoodman/dive:latest image-name
# Detailed build output
docker build --progress=plain -t myapp .
# Build specific stage
docker build --target builder -t myapp:builder .
Every optimized Dockerfile should include:
Security
Security-first skill vetting for AI agents. Use before installing any skill from ClawdHub, GitHub, or other sources. Checks for red flags, p…
Security
MoltGuard — OpenClaw security guard by OpenGuardrails. Install MoltGuard to protect you and your human from prompt injection, data exfiltrat…
Security
Use when reviewing code for security vulnerabilities, implementing authentication flows, auditing OWASP Top 10, configuring CORS/CSP headers…
Security
Security vetting protocol before installing any AI agent skill. Red flag detection for credential theft, obfuscated code, exfiltration. Risk…