agentsclimarketplace

Docker patterns

Skill Lukk17/agent-standards/.agents/skills/docker-patterns

Docker and Docker Compose patterns for local development, container security, networking, volume strategies, and multi-service orchestration.From its SKILL.md

Install
npx -y skills add Lukk17/agent-standards --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

  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

11.1 KB, ~2.8k tokens by cl100k_base, as published. Nobody here has run it

Docker Patterns

Docker and Docker Compose best practices for containerized development.


When to Activate

  • Setting up Docker Compose for local development
  • Designing multi-container architectures
  • Troubleshooting container networking or volume issues
  • Reviewing Dockerfiles for security and size
  • Migrating from local dev to containerized workflow

Docker Compose for Local Development

Standard Web App Stack

Rule: Named volumes only. Never use anonymous volumes (e.g., - /app/node_modules). Why:

  1. Unidentifiable, no name; cannot be targeted by backup scripts or docker volume inspect
  2. Orphaned silently, persist as dangling volumes after docker-compose down; accumulate disk usage invisibly
  3. Not shareable, cannot be referenced between services or compose files
  4. Breaks reproducibility, volume names become random hashes; nothing can reliably reference them
  5. Production foot-gun, data is tied to container lifecycle rather than a managed named resource
# docker-compose.yml
services:
  app:
    build:
      context: .
      target: dev                     # Use dev stage of multi-stage Dockerfile
    ports:
      - "3000:3000"
    volumes:
      - .:/app                        # Bind mount for hot reload
      - node_modules:/app/node_modules  # Named volume -- preserves container deps
    environment:
      - DATABASE_URL=postgres://postgres:postgres@db:5432/app_dev
      - REDIS_URL=redis://redis:6379/0
      - NODE_ENV=development
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started
    command: npm run dev

  db:
    image: postgres:16-alpine
    ports:
      - "5432:5432"
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: app_dev
    volumes:
      - pgdata:/var/lib/postgresql/data
      - ./scripts/init-db.sql:/docker-entrypoint-initdb.d/init.sql
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 3s
      retries: 5

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    volumes:
      - redisdata:/data

  mailpit:                            # Local email testing
    image: axllent/mailpit
    ports:
      - "8025:8025"                   # Web UI
      - "1025:1025"                   # SMTP

volumes:
  pgdata:
  redisdata:
  node_modules:

Development vs Production Dockerfile

# Stage: dependencies
FROM node:22-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci

# Stage: dev (hot reload, debug tools)
FROM node:22-alpine AS dev
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
EXPOSE 3000
CMD ["npm", "run", "dev"]

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

# Stage: production (minimal image)
FROM node:22-alpine AS production
WORKDIR /app
RUN addgroup -g 1001 -S appgroup && adduser -S appuser -u 1001
USER 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 ./
ENV NODE_ENV=production
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s CMD wget -qO- http://localhost:3000/health || exit 1
CMD ["node", "dist/server.js"]

Override Files

# docker-compose.override.yml (auto-loaded, dev-only settings)
services:
  app:
    environment:
      - DEBUG=app:*
      - LOG_LEVEL=debug
    ports:
      - "9229:9229"                   # Node.js debugger

# docker-compose.prod.yml (explicit for production)
services:
  app:
    build:
      target: production
    restart: always
    deploy:
      resources:
        limits:
          cpus: "1.0"
          memory: 512M
# Development (auto-loads override)
docker compose up

# Production
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

Networking

Service Discovery

Services in the same Compose network resolve by service name:

# From "app" container:
postgres://postgres:postgres@db:5432/app_dev    # "db" resolves to the db container
redis://redis:6379/0                             # "redis" resolves to the redis container

Custom Networks

services:
  frontend:
    networks:
      - frontend-net

  api:
    networks:
      - frontend-net
      - backend-net

  db:
    networks:
      - backend-net              # Only reachable from api, not frontend

networks:
  frontend-net:
  backend-net:

Exposing Only What's Needed

services:
  db:
    ports:
      - "127.0.0.1:5432:5432"   # Only accessible from host, not network
    # Omit ports entirely in production -- accessible only within Docker network

Volume Strategies

volumes:
  # Named volume: persists across container restarts, managed by Docker
  pgdata:

  # Bind mount: maps host directory into container (for development)
  # - ./src:/app/src

  # Named volume for container-generated content (never use anonymous volumes)
  # node_modules:/app/node_modules

Common Patterns

services:
  app:
    volumes:
      - .:/app                          # Source code (bind mount for hot reload)
      - node_modules:/app/node_modules  # Named volume: protect container's node_modules from host
      - next_cache:/app/.next           # Named volume: protect build cache

  db:
    volumes:
      - pgdata:/var/lib/postgresql/data          # Persistent data
      - ./scripts/init.sql:/docker-entrypoint-initdb.d/init.sql  # Init scripts

volumes:
  node_modules:
  next_cache:
  pgdata:

Container Security

Dockerfile Hardening

# 1. Use specific tags (never :latest)
FROM node:22.12-alpine3.20

# 2. Run as non-root
RUN addgroup -g 1001 -S app && adduser -S app -u 1001
USER app

# 3. Drop capabilities (in compose)
# 4. Read-only root filesystem where possible
# 5. No secrets in image layers

Compose Security

services:
  app:
    security_opt:
      - no-new-privileges:true
    read_only: true
    tmpfs:
      - /tmp
      - /app/.cache
    cap_drop:
      - ALL
    cap_add:
      - NET_BIND_SERVICE          # Only if binding to ports < 1024

Secret Management

# GOOD: Use environment variables (injected at runtime)
services:
  app:
    env_file:
      - .env                     # Never commit .env to git
    environment:
      - API_KEY                  # Inherits from host environment

# GOOD: Docker secrets (Swarm mode)
secrets:
  db_password:
    file: ./secrets/db_password.txt

services:
  db:
    secrets:
      - db_password

# BAD: Hardcoded in image
# ENV API_KEY=sk-proj-xxxxx      # NEVER DO THIS

.dockerignore

Always create a .dockerignore at the same level as the Dockerfile:

node_modules
dist
build
*.log
.git
.gitignore
.env
.env.*
coverage
**/*.test.ts
**/*.spec.ts
tests/

Base Image Policy

Use casePreferred base
Compiled binaries (Go, Rust)gcr.io/distroless/static or scratch
JVM (Spring Boot)gcr.io/distroless/java21
Node.jsnode:22-alpine
Pythonpython:3.12-slim

Never use ubuntu:latest or debian:latest as a runtime base, use a specific slim or distroless image.


apt-get Best Practice

Always combine install + cleanup in a single RUN layer:

RUN apt-get update && apt-get install -y --no-install-recommends \
    curl \
    ca-certificates \
    && rm -rf /var/lib/apt/lists/*

OCI Image Labels

Every published Dockerfile must include standard labels:

LABEL org.opencontainers.image.source="https://github.com/org/repo" \
      org.opencontainers.image.revision="${GIT_SHA}" \
      org.opencontainers.image.created="${BUILD_DATE}" \
      org.opencontainers.image.version="${VERSION}"

Supply Chain Security

Image Scanning (Trivy)

Scan every image in CI before pushing:

trivy image --exit-code 1 --severity CRITICAL,HIGH myimage:tag
  • Block the pipeline on CRITICAL/HIGH CVEs with available fixes
  • Store the scan report as a CI artifact

SBOM Generation

Generate a Software Bill of Materials and attach as OCI referrer:

syft myimage:tag -o cyclonedx-json > sbom.json

Container Signing (cosign / Sigstore)

Sign every production image after push:

cosign sign --key cosign.key myregistry/myimage:tag

Verify before deploy:

cosign verify --key cosign.pub myregistry/myimage:tag

Build-Time Secrets

Use --mount=type=secret instead of ARG / ENV for sensitive values:

# Dockerfile
RUN --mount=type=secret,id=npm_token \
    NPM_TOKEN=$(cat /run/secrets/npm_token) npm install

# Build command
docker build --secret id=npm_token,src=.npmrc .

Registry Retention Policy

  • Keep last 10 tagged releases in the registry
  • Auto-delete dangling (untagged) images after 7 days
  • Implement via registry lifecycle policies (ECR, GCR, Docker Hub retention)

Debugging

Common Commands

# View logs
docker compose logs -f app           # Follow app logs
docker compose logs --tail=50 db     # Last 50 lines from db

# Execute commands in running container
docker compose exec app sh           # Shell into app
docker compose exec db psql -U postgres  # Connect to postgres

# Inspect
docker compose ps                     # Running services
docker compose top                    # Processes in each container
docker stats                          # Resource usage

# Rebuild
docker compose up --build             # Rebuild images
docker compose build --no-cache app   # Force full rebuild

# Clean up
docker compose down                   # Stop and remove containers
docker compose down -v                # Also remove volumes (DESTRUCTIVE)
docker system prune                   # Remove unused images/containers

Debugging Network Issues

# Check DNS resolution inside container
docker compose exec app nslookup db

# Check connectivity
docker compose exec app wget -qO- http://api:3000/health

# Inspect network
docker network ls
docker network inspect <project>_default

Anti-Patterns

# BAD: Using docker compose in production without orchestration
# Use Kubernetes, ECS, or Docker Swarm for production multi-container workloads

# BAD: Storing data in containers without volumes
# Containers are ephemeral -- all data lost on restart without volumes

# BAD: Running as root
# Always create and use a non-root user

# BAD: Using :latest tag
# Pin to specific versions for reproducible builds

# BAD: One giant container with all services
# Separate concerns: one process per container

# BAD: Putting secrets in docker-compose.yml
# Use .env files (gitignored) or Docker secrets

# BAD: Using anonymous volumes (e.g., - /app/node_modules)
# Always use named volumes so they are identifiable, shareable, and manageable

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Keep looking

Skills are one crate of 326,790. 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.