agentsclimarketplace

Docker patterns

Skill Tibsfox/gsd-skill-creator/examples/skills/patterns/docker-patterns

Introduces a comprehensive agent-based framework for guided software development (GSD)

Install
npx -y skills add Tibsfox/gsd-skill-creator --skill docker-patterns

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.

What its author says it does

Copied from the file, not written here

Provides Docker and containerization best practices including multi-stage builds, security hardening, and compose patterns. Use when writing Dockerfiles, optimizing images, setting up containers, or when user mentions 'Docker', 'container', 'Dockerfile', 'docker-compose', 'image'.

SKILL.md

12.8 KB, ~3.3k tokens by cl100k_base, as published. Nobody here has run it

Docker Patterns

Best practices for building secure, efficient, and production-ready Docker images and compositions.

Multi-Stage Builds

Multi-stage builds separate build dependencies from runtime, producing smaller and more secure images.

Node.js / TypeScript

# Stage 1: Install dependencies
FROM node:20-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --ignore-scripts

# Stage 2: Build
FROM node:20-alpine AS build
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build
RUN npm prune --production

# Stage 3: Production
FROM node:20-alpine AS production
WORKDIR /app

RUN addgroup -g 1001 appgroup && \
    adduser -u 1001 -G appgroup -s /bin/sh -D appuser

COPY --from=build --chown=appuser:appgroup /app/dist ./dist
COPY --from=build --chown=appuser:appgroup /app/node_modules ./node_modules
COPY --from=build --chown=appuser:appgroup /app/package.json ./

USER appuser
EXPOSE 3000

HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1

CMD ["node", "dist/index.js"]

Python

# Stage 1: Build
FROM python:3.12-slim AS build
WORKDIR /app

RUN python -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

# Stage 2: Production
FROM python:3.12-slim AS production
WORKDIR /app

RUN groupadd -r appgroup && useradd -r -g appgroup -s /sbin/nologin appuser

COPY --from=build /opt/venv /opt/venv
COPY --from=build --chown=appuser:appgroup /app .

ENV PATH="/opt/venv/bin:$PATH"
USER appuser
EXPOSE 8000

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

CMD ["gunicorn", "--bind", "0.0.0.0:8000", "--workers", "4", "app:create_app()"]

Go

# Stage 1: Build
FROM golang:1.22-alpine AS build
WORKDIR /app

COPY go.mod go.sum ./
RUN go mod download

COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /server ./cmd/server

# Stage 2: Production (scratch = no OS, minimal attack surface)
FROM scratch AS production

COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=build /server /server

USER 65534:65534
EXPOSE 8080

ENTRYPOINT ["/server"]

Layer Caching Optimization

Docker caches each layer. Order instructions from least-frequently-changed to most-frequently-changed.

Layer Order (Top = Changes Least)

# 1. Base image              (changes: rarely)
FROM node:20-alpine

# 2. System dependencies     (changes: rarely)
RUN apk add --no-cache dumb-init

# 3. Create user             (changes: never)
RUN adduser -D appuser

# 4. Working directory        (changes: never)
WORKDIR /app

# 5. Package manifests       (changes: occasionally)
COPY package.json package-lock.json ./

# 6. Install dependencies    (changes: occasionally, cached if manifests unchanged)
RUN npm ci

# 7. Application code        (changes: frequently)
COPY . .

# 8. Build step              (changes: frequently)
RUN npm run build

# 9. Runtime config          (changes: rarely)
USER appuser
CMD ["node", "dist/index.js"]

Caching Rules

RuleWhy
Copy lock files before source codeDependency install is cached if lock file unchanged
Use npm ci not npm installDeterministic installs, respects lock file exactly
Use --no-cache-dir for pipAvoids storing pip cache in the layer
Combine RUN commands with &&Fewer layers, smaller image
Use .dockerignorePrevents cache busting from irrelevant file changes

What Busts the Cache

ChangeLayers Invalidated
Edit source codeCode COPY and everything after
Edit package.jsonDependency install and everything after
Change base image tagEverything
Add a new RUN before existing onesThat RUN and everything after

Security Hardening

Non-Root User (Required)

Never run containers as root. A compromised container running as root can escalate to host-level access.

# Alpine
RUN addgroup -g 1001 appgroup && \
    adduser -u 1001 -G appgroup -s /bin/sh -D appuser
USER appuser

# Debian/Ubuntu
RUN groupadd -r appgroup && useradd -r -g appgroup -s /sbin/nologin appuser
USER appuser

# Scratch (numeric user, no user database)
USER 65534:65534

Minimal Base Images

Base ImageSizeUse Case
scratch0 MBStatically compiled Go binaries
alpine~5 MBMost applications
distroless~20 MBWhen you need glibc but not a shell
slim~80 MBWhen alpine causes compatibility issues
full~900 MBNever in production

Secrets Management

NEVER put secrets in these locations:

LocationWhy It Is Dangerous
ENV instructionVisible in docker inspect, image history, and all child images
ARG instructionVisible in build history (docker history)
COPY-ed filesPersists in image layer even if deleted in later layer
Build contextAccessible during build if not in .dockerignore

Safe alternatives:

# Runtime secrets via environment variables (set at run time, not build time)
# docker run -e DATABASE_URL=... myapp

# Docker secrets (Swarm/Compose)
# docker secret create db_password ./password.txt

# Mount secrets at build time (BuildKit, not persisted in layers)
RUN --mount=type=secret,id=npmrc,target=/root/.npmrc npm ci

Security Checklist

  • Running as non-root user
  • Using minimal base image (alpine/distroless/scratch)
  • No secrets in ENV, ARG, or COPY instructions
  • No secrets in build context (check .dockerignore)
  • Pinned base image versions (not latest)
  • --no-cache-dir on package installs
  • Read-only filesystem where possible (--read-only flag)
  • No unnecessary packages installed
  • Health check configured
  • Dropped all Linux capabilities not needed (--cap-drop=ALL)

Image Scanning

Scan images for known vulnerabilities before deploying.

# Docker Scout (built into Docker Desktop)
docker scout cves myimage:latest

# Trivy (open source)
trivy image myimage:latest

# Snyk
snyk container test myimage:latest

Health Checks

Dockerfile HEALTHCHECK

HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
  CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1

Parameters

ParameterDefaultDescription
--interval30sTime between checks
--timeout30sMax time for a check to complete
--start-period0sGrace period for container startup
--retries3Consecutive failures before unhealthy

Health Check Commands by Stack

StackCommand
Node.jswget --spider http://localhost:3000/health
Pythonpython -c "import urllib.request; urllib.request.urlopen('http://...')"
GoBinary built with health endpoint
Nginxcurl -f http://localhost/ || exit 1
PostgreSQLpg_isready -U postgres
Redisredis-cli ping

Health Endpoint Best Practices

  • Return 200 for healthy, 503 for unhealthy
  • Check downstream dependencies (database, cache) in the health endpoint
  • Keep checks fast (< 1 second)
  • Separate liveness (is the process alive?) from readiness (can it serve traffic?)

Docker Compose Patterns

Development Compose

# compose.yaml (development)
services:
  app:
    build:
      context: .
      target: deps  # Stop at dependency stage for dev
    volumes:
      - .:/app
      - /app/node_modules  # Prevent host node_modules from overriding
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=development
    depends_on:
      db:
        condition: service_healthy
    command: npm run dev

  db:
    image: postgres:16-alpine
    volumes:
      - pgdata:/var/lib/postgresql/data
    environment:
      POSTGRES_DB: myapp_dev
      POSTGRES_USER: devuser
      POSTGRES_PASSWORD: devpassword  # Dev only; never use simple passwords in production
    ports:
      - "5432:5432"
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U devuser -d myapp_dev"]
      interval: 5s
      timeout: 3s
      retries: 5

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 3s
      retries: 5

volumes:
  pgdata:

Production Compose

# compose.prod.yaml
services:
  app:
    build:
      context: .
      target: production
    restart: unless-stopped
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=production
    env_file:
      - .env.production  # Secrets loaded from file, not hardcoded
    depends_on:
      db:
        condition: service_healthy
    deploy:
      resources:
        limits:
          memory: 512M
          cpus: "0.5"
    read_only: true
    tmpfs:
      - /tmp
    security_opt:
      - no-new-privileges:true

Compose Best Practices

PracticeWhy
Use depends_on with condition: service_healthyPrevents app starting before dependencies are ready
Set resource limitsPrevents one container from consuming all resources
Use named volumes for dataAnonymous volumes are hard to manage
Use restart: unless-stopped in productionAuto-restart on failure, but not after manual stop
Separate dev and prod compose filesDifferent targets, volumes, and security settings
Use read_only: true where possiblePrevents runtime filesystem modifications

.dockerignore

Always include a .dockerignore to keep the build context small and prevent leaking sensitive files.

# Version control
.git
.gitignore

# Dependencies (installed in container)
node_modules
__pycache__
*.pyc
venv/

# Environment and secrets
.env
.env.*
*.pem
*.key
credentials.json

# IDE and editor files
.vscode/
.idea/
*.swp
*.swo

# Build output
dist/
build/
coverage/
*.log

# Docker files (no need to copy into context)
Dockerfile
docker-compose*.yml
compose*.yaml
.dockerignore

# Documentation
*.md
LICENSE
docs/

# Tests (unless needed for build)
tests/
test/
__tests__/
*.test.*
*.spec.*

Anti-Patterns

Anti-PatternProblemFix
Running as rootContainer compromise = host compromiseAdd non-root user, USER appuser
Using latest tagNon-reproducible buildsPin versions: node:20.11-alpine
Secrets in ENV/ARGVisible in image metadata and historyUse runtime env vars or Docker secrets
Single-stage buildsLarge images with build tools in productionUse multi-stage builds
No .dockerignoreLarge context, potential secret leaksAlways include .dockerignore
COPY . . before npm installCache busted on every code changeCopy package files first, install, then copy code
Installing dev dependencies in productionLarger image, larger attack surfaceUse npm ci --omit=dev or npm prune --production
No health checkOrchestrator cannot detect unhealthy containersAdd HEALTHCHECK instruction
Storing data in containersData lost when container is removedUse volumes for persistent data
Ignoring image sizeSlow pulls, more storage, larger attack surfaceUse alpine/distroless, multi-stage, .dockerignore
apt-get install without cleanupCached package lists bloat the layerapt-get update && apt-get install -y ... && rm -rf /var/lib/apt/lists/*
Using ADD instead of COPYADD has magic behavior (auto-extract, URL fetch)Use COPY unless you specifically need ADD features

Production Readiness Checklist

Before deploying a containerized application:

  • Multi-stage build separates build and runtime
  • Running as non-root user
  • Base image pinned to specific version
  • No secrets baked into the image
  • .dockerignore prevents context leaks
  • Health check configured
  • Resource limits set (memory, CPU)
  • Graceful shutdown handled (SIGTERM)
  • Logs written to stdout/stderr (not files)
  • Image scanned for vulnerabilities
  • Read-only filesystem where possible
  • No unnecessary packages or tools installed

Gives 2 of the 12 instructions most security skills give in ~3.3k tokens

Counted across 648 of the 828 authors here whose files we hold, read 2026-08-06

  • parameterize all database queriesin 67 of 648, across 49 files
  • hash passwords using bcrypt scrypt or argon2in 48 of 648, across 35 files
  • apply rate limiting to authentication endpointsin 48 of 648, across 24 files
  • Configure security headersin 35 of 648, across 18 files
  • validate all inputsin 32 of 648, across 24 files
  • validate all external input at the system boundaryin 29 of 648, across 18 files
  • run containers as a non-root userhere, and in 28 of 648, across 15 files
  • use httponly secure samesite cookies for sessionsin 26 of 648, across 15 files
  • run dependency audits before every releasein 21 of 648, across 10 files
  • encode output to prevent cross-site scriptingin 21 of 648, across 10 files
  • copy dependencies before source codehere, and in 20 of 648, across 9 files
  • store secrets in environment variablesin 20 of 648, across 17 files

Said here and by no other author read

  • Use multi-stage builds to separate build dependencies from runtime
  • Configure a health check in the Dockerfile
  • Combine run commands to reduce image layers

Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once. Length counted with cl100k_base; the agent that loads this file may tokenize it differently.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.