Skip to content
Back to skills

Docker Expert

ASecurity

Expert-level Docker and containerization. Use when writing Dockerfiles, docker-compose files, multi-stage builds, optimizing image size, container networking, volumes, secrets, health checks, or debugging container issues. Also use when the user mentions 'Dockerfile', 'docker-compose', 'container', 'image size', 'layer cache', 'registry', 'container wont start', or 'docker build'.

  • 11 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added September 10, 2026
devopspythonrustgojavashellbashsqlnodenodejsdocker

Works with

  • vscode
  • cli
  • api

Security analysis

A96/100
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro shows the line behind each finding and how to fix it

Scanned September 10, 2026

npx -y skills add luokai0/ai-agent-skills-by-luo-kai --skill docker-expert --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Docker Expert?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Docker Expert
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/luokai0-docker-expert/badge)](https://www.skillsdirectory.com/skills/luokai0-docker-expert)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
author: luo-kai
name: docker-expert
description: Expert-level Docker and containerization. Use when writing Dockerfiles, docker-compose files, multi-stage builds, optimizing image size, container networking, volumes, secrets, health checks, or debugging container issues. Also use when the user mentions 'Dockerfile', 'docker-compose', 'container', 'image size', 'layer cache', 'registry', 'container wont start', or 'docker build'.
license: MIT
metadata:
  author: luokai0
  version: "1.0"
  category: coding
---

# Docker Expert

You are an expert in Docker and containerization with deep knowledge of container internals, image optimization, and production deployment patterns.

## Before Starting

1. **Language/runtime** — Node.js, Python, Go, Java, Rust?
2. **Use case** — development, production, CI/CD?
3. **Compose or single container?**
4. **Specific issue?** — image too large, build too slow, networking problem, container crashes?

---

## Core Expertise Areas

- **Dockerfile optimization**: layer caching order, multi-stage builds, .dockerignore, ARG vs ENV
- **Security**: non-root users, read-only filesystems, minimal base images, image scanning
- **docker-compose**: service dependencies, health checks, networks, profiles, override files
- **Networking**: bridge, host, overlay networks, DNS resolution between containers
- **Volumes**: named volumes, bind mounts, tmpfs, volume drivers
- **Production patterns**: resource limits, restart policies, init processes (tini/dumb-init)
- **Registry**: image tagging strategies, multi-arch builds with buildx, SBOM
- **Debugging**: exec, logs, inspect, events, stats, copying files out of containers

---

## Key Patterns & Code

### Optimized Multi-Stage Dockerfile — Node.js
```dockerfile
# ─── Stage 1: Install production dependencies ───────────────────────────────
FROM node:20-alpine AS deps
WORKDIR /app

# Copy ONLY package files first — layer cached until these change
COPY package.json package-lock.json ./
RUN npm ci --only=production && npm cache clean --force

# ─── Stage 2: Build ──────────────────────────────────────────────────────────
FROM node:20-alpine AS builder
WORKDIR /app

COPY package.json package-lock.json ./
RUN npm ci

COPY . .
RUN npm run build

# ─── Stage 3: Production image (minimal) ─────────────────────────────────────
FROM node:20-alpine AS runner
WORKDIR /app

# Security: create non-root user
RUN addgroup --system --gid 1001 nodejs \
    && adduser --system --uid 1001 --ingroup nodejs appuser

# Copy only what is needed from previous stages
COPY --from=deps    --chown=appuser:nodejs /app/node_modules ./node_modules
COPY --from=builder --chown=appuser:nodejs /app/dist        ./dist
COPY --from=builder --chown=appuser:nodejs /app/package.json ./

USER appuser
EXPOSE 3000
ENV NODE_ENV=production PORT=3000

# Health check — orchestrators use this to know when app is ready
HEALTHCHECK \
  --interval=30s \
  --timeout=10s \
  --start-period=40s \
  --retries=3 \
  CMD wget -qO- http://localhost:3000/health || exit 1

ENTRYPOINT ["node", "dist/server.js"]
```

### Optimized Multi-Stage Dockerfile — Python
```dockerfile
# ─── Stage 1: Build dependencies ─────────────────────────────────────────────
FROM python:3.12-slim AS builder
WORKDIR /app

RUN pip install --upgrade pip
COPY requirements.txt .
RUN pip install --prefix=/install --no-cache-dir -r requirements.txt

# ─── Stage 2: Production image ───────────────────────────────────────────────
FROM python:3.12-slim AS runner
WORKDIR /app

# Non-root user
RUN useradd --system --uid 1001 --no-create-home appuser

# Copy installed packages from builder
COPY --from=builder /install /usr/local

COPY --chown=appuser:appuser . .

USER appuser
EXPOSE 8000

HEALTHCHECK --interval=30s --timeout=10s --retries=3 \
  CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')"

CMD ["python", "-m", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
```

### Optimized Multi-Stage Dockerfile — Go
```dockerfile
# ─── Stage 1: Build ──────────────────────────────────────────────────────────
FROM golang:1.22-alpine AS builder
WORKDIR /app

# Download dependencies first (cached layer)
COPY go.mod go.sum ./
RUN go mod download

COPY . .

# Build static binary — no external dependencies
RUN CGO_ENABLED=0 GOOS=linux go build \
    -ldflags="-w -s" \
    -o server ./cmd/server

# ─── Stage 2: Minimal production image ───────────────────────────────────────
# distroless: no shell, no package manager — smallest attack surface
FROM gcr.io/distroless/static-debian12 AS runner

COPY --from=builder /app/server /server

EXPOSE 8080
USER nonroot:nonroot

ENTRYPOINT ["/server"]
```

### .dockerignore — Always Create This
```
# Version control
.git
.gitignore

# Dependencies (always reinstalled in container)
node_modules
vendor
__pycache__
*.pyc
.venv
venv

# Build artifacts
dist
build
.next
out
target

# Test & coverage
coverage
*.test
.pytest_cache
__tests__

# Environment files — NEVER include these
.env
.env.*
*.env

# Logs
*.log
logs

# Editor
.vscode
.idea
*.swp
*.swo

# Docker files themselves
docker-compose*.yml
Dockerfile*

# Docs
README.md
CHANGELOG.md
docs
```

### docker-compose — Production Pattern
```yaml
version: "3.9"

services:
  api:
    build:
      context: .
      target: runner        # use specific build stage
      cache_from:
        - type=registry,ref=myregistry/myapp:cache
    image: myregistry/myapp:${TAG:-latest}
    ports:
      - "3000:3000"
    environment:
      NODE_ENV: production
      DATABASE_URL: postgresql://user:pass@db:5432/mydb
      REDIS_URL: redis://cache:6379
    depends_on:
      db:
        condition: service_healthy
      cache:
        condition: service_healthy
    restart: unless-stopped
    deploy:
      resources:
        limits:
          memory: 512M
          cpus: "0.5"
        reservations:
          memory: 256M
    read_only: true           # security: read-only filesystem
    tmpfs:
      - /tmp                  # writable temp dir
    networks:
      - app-net
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: mydb
      POSTGRES_USER: user
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password
    secrets:
      - db_password
    volumes:
      - pgdata:/var/lib/postgresql/data
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql:ro
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U user -d mydb"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 30s
    restart: unless-stopped
    networks:
      - app-net

  cache:
    image: redis:7-alpine
    command: redis-server --maxmemory 256mb --maxmemory-policy allkeys-lru
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5
    restart: unless-stopped
    networks:
      - app-net

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./certs:/etc/nginx/certs:ro
    depends_on:
      - api
    restart: unless-stopped
    networks:
      - app-net

volumes:
  pgdata:
    driver: local

networks:
  app-net:
    driver: bridge

secrets:
  db_password:
    file: ./secrets/db_password.txt
```

### docker-compose Override for Development
```yaml
# docker-compose.override.yml — auto-loaded in development
version: "3.9"

services:
  api:
    build:
      target: builder       # use dev stage with devDependencies
    volumes:
      - .:/app              # bind mount source for hot reload
      - /app/node_modules   # anonymous volume to preserve container's node_modules
    environment:
      NODE_ENV: development
      DEBUG: "*"
    command: npm run dev    # override production command
    ports:
      - "9229:9229"         # Node.js debugger port

  db:
    ports:
      - "5432:5432"         # expose for local tools like pgAdmin

  cache:
    ports:
      - "6379:6379"         # expose for local Redis tools
```

### Networking Patterns
```bash
# Containers on same network communicate by service name
# api → db:5432 (service name = hostname)
# api → cache:6379

# Inspect container network
docker inspect mycontainer | jq '.[0].NetworkSettings.Networks'

# Connect running container to another network
docker network connect my-network my-container

# Create custom network with specific subnet
docker network create \
  --driver bridge \
  --subnet 172.20.0.0/16 \
  --ip-range 172.20.240.0/20 \
  my-custom-net

# List all networks
docker network ls

# Remove unused networks
docker network prune
```

### Debugging Commands
```bash
# Get interactive shell in running container
docker exec -it <container_name> sh

# Debug a stopped or crashing container
# Override entrypoint to get shell
docker run --rm -it --entrypoint sh myimage:latest

# Follow logs with timestamps
docker logs -f --timestamps --tail=100 <container_name>

# Copy files out of container for inspection
docker cp <container_name>:/app/logs/error.log ./error.log

# Real-time resource usage
docker stats --no-stream

# Inspect everything about a container
docker inspect <container_name>

# View image layers and sizes
docker history myimage:latest --human --no-trunc

# Check what changed in container filesystem vs image
docker diff <container_name>

# Scan image for vulnerabilities
docker scout cves myimage:latest

# Build with detailed output for debugging cache
docker build --progress=plain --no-cache .

# Check why container exited
docker inspect <container_name> | jq '.[0].State'
```

### Multi-Arch Build with Buildx
```bash
# Create and use a buildx builder
docker buildx create --name mybuilder --use
docker buildx inspect --bootstrap

# Build for multiple architectures and push
docker buildx build \
  --platform linux/amd64,linux/arm64 \
  --tag myregistry/myapp:latest \
  --push \
  .

# Build with cache export to registry
docker buildx build \
  --cache-from type=registry,ref=myregistry/myapp:cache \
  --cache-to type=registry,ref=myregistry/myapp:cache,mode=max \
  --tag myregistry/myapp:latest \
  --push \
  .
```

### Health Check Patterns
```dockerfile
# HTTP health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
  CMD wget -qO- http://localhost:3000/health || exit 1

# TCP health check (when no HTTP endpoint)
HEALTHCHECK --interval=10s --timeout=5s --retries=5 \
  CMD nc -z localhost 5432 || exit 1

# Custom script health check
COPY healthcheck.sh /healthcheck.sh
RUN chmod +x /healthcheck.sh
HEALTHCHECK --interval=30s CMD /healthcheck.sh
```

---

## Best Practices

- Always use specific image tags — never `latest` in production (`node:20-alpine` not `node:latest`)
- Always create `.dockerignore` — dramatically speeds up builds and reduces context size
- Run as non-root user in every production container
- Use multi-stage builds — final image should contain only what is needed to run
- Set memory and CPU limits — prevent one container from starving others
- Add `HEALTHCHECK` to every production image
- Use `read_only: true` + `tmpfs` for `/tmp` — better security posture
- Never put secrets in `ENV` or `ARG` — use Docker secrets or secret mounts
- Pin base image digests for reproducibility in critical systems
- Use `dumb-init` or `tini` as PID 1 to handle signals properly

---

## Common Pitfalls

| Pitfall | Problem | Fix |
|---|---|---|
| No `.dockerignore` | Huge build context, slow builds, secrets leaked | Always create `.dockerignore` |
| Running as root | Security vulnerability | Add non-root user in Dockerfile |
| Copy source before package.json | Cache busted on every code change | Copy package.json first, then source |
| Secrets in `ENV` or `ARG` | Visible in `docker inspect` and image history | Use `--secret` mount or Docker secrets |
| No health check | Orchestrator cannot detect unhealthy app | Add `HEALTHCHECK` instruction |
| `latest` tag | Non-deterministic, impossible to rollback | Always pin exact version tags |
| One huge RUN command | Hard to debug layer failures | Split logical steps into separate RUN |
| No resource limits | One container can OOM the host | Always set memory limits in production |

---

## Related Skills

- **kubernetes-expert**: For orchestrating containers at scale
- **cicd-expert**: For Docker in CI/CD pipelines
- **nginx-expert**: For Nginx as a reverse proxy in containers
- **linux-expert**: For Linux container internals
- **aws-expert**: For ECR, ECS, and EKS on AWS

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.