Loading...
Loading...
Docker containerization expert: Dockerfile optimization, multi-stage builds, security hardening, Docker Compose orchestration, and production deployment. Use for Dockerfile creation/review, image size issues, container security, networking, and orchestration.
npx skill4agent add fellipeutaka/leon dockerlatest| Base Image | Size | Shell | Use Case |
|---|---|---|---|
| ~10-30MB | Yes | Zero-CVE goal, SBOM included |
| ~7MB | Yes | General-purpose minimal |
| ~2-5MB | No | Hardened production, no debug |
| ~70-100MB | Yes | When Alpine compatibility is an issue |
| 0MB | No | Static binaries (Go, Rust) |
node:22.14.0-alpine3.21node:alpinelatest# 1. Base image + system deps (rarely change)
FROM node:22-alpine
RUN apk add --no-cache dumb-init
# 2. Dependencies (change occasionally)
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --only=production
# 3. Application code (changes frequently)
COPY . .
# 4. Metadata + runtime config
USER node
EXPOSE 3000
CMD ["dumb-init", "node", "server.js"].git
node_modules
__pycache__
*.pyc
.vscode
.idea
.DS_Store
*.log
coverage/
.env
.env.local
dist/
build/
README.md
docs/# Node.js
RUN \
npm ci
# Python
RUN \
pip install -r requirements.txt
# Go
RUN \
go build -o /app .DOCKER_BUILDKIT=1FROM node:22-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
FROM node:22-alpine AS build
WORKDIR /app
COPY /app/node_modules ./node_modules
COPY . .
RUN npm run build
FROM node:22-alpine AS runtime
RUN addgroup -g 1001 -S app && adduser -S app -u 1001
WORKDIR /app
COPY /app/dist ./dist
COPY /app/node_modules ./node_modules
COPY package.json ./
USER app
EXPOSE 3000
HEALTHCHECK \
CMD wget -qO- http://localhost:3000/health || exit 1
CMD ["node", "dist/index.js"]FROM python:3.13-slim AS build
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir --prefix=/install -r requirements.txt
FROM python:3.13-slim AS runtime
RUN useradd -r -u 1001 app
WORKDIR /app
COPY /install /usr/local
COPY . .
USER app
EXPOSE 8000
CMD ["python", "-m", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]FROM golang:1.24-alpine AS build
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -ldflags="-s -w" -o /app .
FROM scratch
COPY /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY /app /app
USER 65534:65534
EXPOSE 8080
ENTRYPOINT ["/app"]FROM eclipse-temurin:21-jdk-alpine AS build
WORKDIR /app
COPY . .
RUN ./gradlew build --no-daemon
FROM eclipse-temurin:21-jre-alpine AS runtime
RUN addgroup -g 1001 -S app && adduser -S app -u 1001
WORKDIR /app
COPY /app/build/libs/*.jar app.jar
USER app
EXPOSE 8080
HEALTHCHECK \
CMD wget -qO- http://localhost:8080/actuator/health || exit 1
CMD ["java", "-jar", "app.jar"]FROM rust:1.84-alpine AS build
RUN apk add --no-cache musl-dev
WORKDIR /app
COPY Cargo.toml Cargo.lock ./
RUN mkdir src && echo "fn main() {}" > src/main.rs && cargo build --release && rm -rf src
COPY src ./src
RUN cargo build --release
FROM scratch
COPY /app/target/release/app /app
USER 65534:65534
EXPOSE 8080
ENTRYPOINT ["/app"]# Alpine
RUN addgroup -g 1001 -S app && adduser -S app -u 1001 -G app
USER app
# Debian/Ubuntu
RUN groupadd -g 1001 app && useradd -r -u 1001 -g app app
USER app
# Distroless/scratch (numeric only)
USER 65534:65534# BuildKit secrets (never stored in layers)
RUN \
API_KEY=$(cat /run/secrets/api_key) && \
./configure --api-key="$API_KEY"# Build with secret
docker build --secret id=api_key,src=./api_key.txt .ENV API_KEY=secret123 # Visible in image history
COPY .env /app/.env # Baked into layer
ARG PASSWORD=hunter2 # Visible in build historydocker run \
--user 1001:1001 \
--cap-drop=ALL \
--cap-add=NET_BIND_SERVICE \
--read-only \
--tmpfs /tmp:noexec,nosuid \
--security-opt="no-new-privileges:true" \
--memory="512m" \
--cpus="1.0" \
my-image# Docker Scout
docker scout quickview my-image
docker scout cves my-image
# Trivy
trivy image my-image
# Grype
grype my-imageservices:
app:
build:
context: .
target: runtime
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
networks:
- frontend
- backend
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
deploy:
resources:
limits:
cpus: "1.0"
memory: 512M
reservations:
cpus: "0.5"
memory: 256M
restart: unless-stopped
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
db:
image: postgres:17-alpine
environment:
POSTGRES_DB_FILE: /run/secrets/db_name
POSTGRES_USER_FILE: /run/secrets/db_user
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_name
- db_user
- db_password
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- backend
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
command: redis-server --maxmemory 128mb --maxmemory-policy allkeys-lru
volumes:
- redis_data:/data
networks:
- backend
networks:
frontend:
driver: bridge
backend:
driver: bridge
internal: true # No external access
volumes:
postgres_data:
redis_data:
secrets:
db_name:
file: ./secrets/db_name.txt
db_user:
file: ./secrets/db_user.txt
db_password:
file: ./secrets/db_password.txtnetworks:
frontend: # Web-facing services
backend:
internal: true # Database, cache — no external access
services:
proxy:
networks: [frontend]
api:
networks: [frontend, backend]
db:
networks: [backend] # Only reachable from api# compose.yml (base)
services:
app:
build: .
# compose.override.yml (dev, loaded automatically)
services:
app:
build:
target: development
volumes:
- .:/app
- /app/node_modules
ports:
- "9229:9229" # Debug
environment:
- NODE_ENV=development
command: npm run dev
# compose.prod.yml
services:
app:
build:
target: runtime
environment:
- NODE_ENV=production
restart: unless-stopped# Dev (auto-loads compose.override.yml)
docker compose up
# Prod
docker compose -f compose.yml -f compose.prod.yml up -d# Bad — 3 layers, cleanup doesn't reduce size
RUN apt-get update
RUN apt-get install -y curl
RUN rm -rf /var/lib/apt/lists/*
# Good — 1 layer, cleanup is effective
RUN apt-get update && \
apt-get install -y --no-install-recommends curl && \
rm -rf /var/lib/apt/lists/*CMD ["node", "server.js"] # Exec form — PID 1, receives signals directly
CMD node server.js # Shell form — spawns /bin/sh, signal issuesservices:
app:
build:
target: development
volumes:
- .:/app # Source code
- /app/node_modules # Prevent overwrite
ports:
- "3000:3000"
- "9229:9229" # Debug port
environment:
- NODE_ENV=development
- DEBUG=app:*
command: npm run devservices:
app:
# Node.js inspect
command: node --inspect=0.0.0.0:9229 server.js
ports:
- "9229:9229"
# Python debugpy
# command: python -m debugpy --listen 0.0.0.0:5678 main.py
# ports:
# - "5678:5678"// /etc/docker/daemon.json
{
"userns-remap": "default",
"storage-driver": "overlay2",
"live-restore": true,
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}overlay2volumes:
- ./src:/app/src:delegated # Better write performance
- ./build:/app/build:cached # Container writes cached:delegated:cachedmutagendocker compose watch.gitattributes* text=auto eol=lf# .gitattributes — prevent CRLF issues in containers
* text=auto eol=lf
*.sh text eol=lf
Dockerfile text eol=lflatest.dockerignoredumb-inittini--cap-drop=ALL| Don't | Do Instead |
|---|---|
| Run as root | |
Use | Pin exact versions |
| |
| Mount Docker socket | Use Docker-in-Docker or alternatives |
| Hardcode secrets in ENV/ARG | BuildKit secrets or runtime mounts |
| Skip health checks | |
| Ignore resource limits | Set |
| Copy entire context | Use |
| Install unnecessary packages | |
| Use shell form CMD | Use exec form |
DOCKER_BUILDKIT=1.dockerignoredocker history <image>docker network inspectdocker logs <container>