.agents/skills/containerization-patterns/SKILL.md
# Skill: Containerization Patterns **Description**: Guidance for agents implementing multi-stage Containerfiles with separate production and development configurations, base image selection, and container optimization. **When to use**: When creating Containerfiles for deployable artifacts, implementing multi-stage builds, selecting base images, or configuring development containers with hot-reload. --- ## Quick Start ### I'm creating a production Containerfile for a Go service 1. Read `.ope
npx skillsauth add em-jones/staccato-toolkit .agents/skills/containerization-patternsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Description: Guidance for agents implementing multi-stage Containerfiles with separate production and development configurations, base image selection, and container optimization.
When to use: When creating Containerfiles for deployable artifacts, implementing multi-stage builds, selecting base images, or configuring development containers with hot-reload.
.opencode/rules/patterns/delivery/container-images.md — Production Containerfile pattern (Go services).opencode/rules/patterns/infrastructure/base-images.md — Go services: Distroless for production# Stage 1: Build
FROM golang:1.23-alpine AS builder
WORKDIR /build
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o app .
# Stage 2: Runtime
FROM gcr.io/distroless/static:nonroot
COPY --from=builder /build/app /app
EXPOSE 8080
CMD ["/app"]
docker build -f Containerfile.prod -t app:prod . && docker images app:prod
.opencode/rules/patterns/development/hot-reload.md — Go services: Use file watcher for rebuild + restartFROM golang:1.23-alpine
RUN apk add --no-cache bash curl git && \
wget -qO- https://github.com/watchexec/watchexec/releases/download/v1.25.1/watchexec-1.25.1-x86_64-unknown-linux-musl.tar.xz | tar xJv && \
mv watchexec-1.25.1-x86_64-unknown-linux-musl/watchexec /usr/local/bin/
WORKDIR /workspace
EXPOSE 8080
CMD ["watchexec", "-r", "-e", "go", "--", "go", "run", "main.go"]
docker run -v $(pwd):/workspace -p 8080:8080 app:dev.opencode/rules/patterns/delivery/container-images.md — Production Containerfile pattern (Node.js services).opencode/rules/patterns/infrastructure/base-images.md — Node.js services: Slim for production# Stage 1: Build
FROM node:22-alpine AS builder
WORKDIR /build
COPY package.json yarn.lock ./
RUN yarn install --frozen-lockfile --production=false
COPY . .
RUN yarn build
# Stage 2: Runtime
FROM node:22-slim
WORKDIR /app
COPY --from=builder /build/dist ./dist
COPY --from=builder /build/node_modules ./node_modules
COPY package.json ./
EXPOSE 7007
USER node
CMD ["node", "dist/index.js"]
docker build -f Containerfile.prod -t app:prod . && docker images app:prod
.opencode/rules/patterns/development/hot-reload.md — Node.js: Use built-in development serversFROM node:22
WORKDIR /workspace
EXPOSE 7007 3000
CMD ["yarn", "serve"]
docker run -v $(pwd):/workspace -p 7007:7007 -p 3000:3000 app:dev.opencode/rules/patterns/infrastructure/base-images.md for the selection matrix:
gcr.io/distroless/static:nonrootgolang:1.23-alpinenode:22-slimnode:22golang:1.23.5-alpine3.19)docker run --rm app:prod idPurpose: Minimize final image size by separating build and runtime stages.
Structure:
Key principles:
Benefits:
Purpose: Enable rapid iteration with hot-reload and debugging tooling.
Structure:
WORKDIR /workspace)Key principles:
Benefits:
Decision tree:
Is this a production image?
├─ Yes → Go service?
│ ├─ Yes → CGO enabled?
│ │ ├─ Yes → gcr.io/distroless/base:nonroot
│ │ └─ No → gcr.io/distroless/static:nonroot
│ └─ No → Node.js service?
│ └─ Yes → node:22-slim
│
└─ No (development) → Go service?
├─ Yes → golang:1.23-alpine
└─ No → Node.js service?
└─ Yes → node:22
Key selection criteria:
Go services (compiled language):
*.go files for changesNode.js services (interpreted language):
yarn serve, npm run dev)Key principles:
/workspaceGo services:
CGO_ENABLED=0 GOOS=linux-ldflags="-s -w"Node.js services:
COPY package.json yarn.lock before COPY . .--production=true in runtime stageKey principles:
.dockerignorelatest tagsNon-root user enforcement:
:nonroot tagUSER node (UID 1000)adduser -S appuser and USER appuserVerification:
docker run --rm app:prod id
# Expected: uid=65532(nonroot) or uid=1000(node)
Key principles:
latest)Production (Containerfile.prod):
-ldflags="-s -w", Node.js: --production)Development (Containerfile.dev):
WORKDIR /workspace)Production image:
# Build
docker build -f Containerfile.prod -t app:prod .
# Check size
docker images app:prod
# Verify non-root user
docker run --rm app:prod id
# Test startup
docker run -p 8080:8080 app:prod
curl http://localhost:8080/healthz
Development image:
# Build
docker build -f Containerfile.dev -t app:dev .
# Run with volume mount
docker run -v $(pwd):/workspace -p 8080:8080 app:dev
# Edit source file
echo "// test change" >> main.go
# Verify hot-reload triggered (check logs)
docker logs <container-id>
Production:
FROM golang:1.23-alpine AS builder
WORKDIR /build
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o cli .
FROM gcr.io/distroless/static:nonroot
COPY --from=builder /build/cli /cli
CMD ["/cli"]
Development:
FROM golang:1.23-alpine
RUN apk add --no-cache bash curl git
WORKDIR /workspace
CMD ["/bin/sh"]
Production: Same as CLI, but expose port and set CMD to server binary
Development:
FROM golang:1.23-alpine
RUN apk add --no-cache bash curl git && \
wget -qO- https://github.com/watchexec/watchexec/releases/download/v1.25.1/watchexec-1.25.1-x86_64-unknown-linux-musl.tar.xz | tar xJv && \
mv watchexec-1.25.1-x86_64-unknown-linux-musl/watchexec /usr/local/bin/
WORKDIR /workspace
EXPOSE 8080
CMD ["watchexec", "-r", "-e", "go", "--", "go", "run", "main.go"]
Production:
FROM node:22-alpine AS builder
WORKDIR /build
COPY package.json yarn.lock ./
RUN yarn install --frozen-lockfile --production=false
COPY . .
RUN yarn build
FROM node:22-slim
WORKDIR /app
COPY --from=builder /build/dist ./dist
COPY --from=builder /build/node_modules ./node_modules
COPY package.json ./
EXPOSE 7007
USER node
CMD ["node", "dist/index.js"]
Development:
FROM node:22
WORKDIR /workspace
EXPOSE 7007 3000
CMD ["yarn", "serve"]
Symptom: Image exceeds size targets (>50MB Go, >500MB Node.js)
Diagnosis:
docker history app:prod
# Identify large layers
Solutions:
.dockerignore to exclude build artifactsSymptom: Container fails to start with "exec format error"
Cause: Binary compiled for wrong architecture (amd64 vs. arm64)
Solution:
# Build for correct architecture
docker buildx build --platform linux/amd64 -f Containerfile.prod -t app:prod .
# Or for multi-arch:
docker buildx build --platform linux/amd64,linux/arm64 -f Containerfile.prod -t app:prod .
Symptom: Source changes don't trigger rebuild/reload
Diagnosis:
# Verify volume mount
docker inspect <container-id> | grep Mounts -A 10
# Check if watch tool is running
docker exec <container-id> ps aux | grep watchexec
Solutions:
-v $(pwd):/workspace)Symptom: Container can't write to filesystem
Cause: Non-root user doesn't have write permissions
Solution:
/tmp (writable in distroless)--user $(id -u):$(id -g)Symptom: Binary fails with "cannot find package" or "module not found"
Cause: Binary is dynamically linked but using distroless/static
Solution:
CGO_ENABLED=0 for static linkinggcr.io/distroless/base:nonroot (includes glibc)tools
<!--VITE PLUS START--> # Using Vite+, the Unified Toolchain for the Web This project is using Vite+, a unified toolchain built on top of Vite, Rolldown, Vitest, tsdown, Oxlint, Oxfmt, and Vite Task. Vite+ wraps runtime management, package management, and frontend tooling in a single global CLI called `vp`. Vite+ is distinct from Vite, but it invokes Vite through `vp dev` and `vp build`. ## Vite+ Workflow `vp` is a global binary that handles the full development lifecycle. Run `vp help` to pr
development
Guide for building performant data tables. Uses tanstack-table for table logic (sorting, filtering, pagination) and tanstack-virtual for rendering large datasets efficiently.
development
Expert guidance for building observable, expressive, and fault-tolerant TypeScript applications using the effect-ts/effect ecosystem. Covers Effect<A, E, R> type, error management, dependency injection via Layers, observability (logging, metrics, tracing), concurrency with Fibers, retry/scheduling, Schema validation, Streams, and Sinks.
tools
Complete E2E (end-to-end) and integration testing skill for TypeScript/NestJS projects using Jest, real infrastructure via Docker, and GWT pattern. ALWAYS use this skill when user needs to: **SETUP** - Initialize or configure E2E testing infrastructure: - Set up E2E testing for a new project - Configure docker-compose for testing (Kafka, PostgreSQL, MongoDB, Redis) - Create jest-e2e.config.ts or E2E Jest configuration - Set up test helpers for database, Kafka, or Redis - Configure .env.e2e environment variables - Create test/e2e directory structure **WRITE** - Create or add E2E/integration tests: - Write, create, add, or generate e2e tests or integration tests - Test API endpoints, workflows, or complete features end-to-end - Test with real databases, message brokers, or external services - Test Kafka consumers/producers, event-driven workflows - Working on any file ending in .e2e-spec.ts or in test/e2e/ directory - Use GWT (Given-When-Then) pattern for tests **REVIEW** - Audit or evaluate E2E tests: - Review existing E2E tests for quality - Check test isolation and cleanup patterns - Audit GWT pattern compliance - Evaluate assertion quality and specificity - Check for anti-patterns (multiple WHEN actions, conditional assertions) **RUN** - Execute or analyze E2E test results: - Run E2E tests - Start/stop Docker infrastructure for testing - Analyze E2E test results - Verify Docker services are healthy - Interpret test output and failures **DEBUG** - Fix failing or flaky E2E tests: - Fix failing E2E tests - Debug flaky tests or test isolation issues - Troubleshoot connection errors (database, Kafka, Redis) - Fix timeout issues or async operation failures - Diagnose race conditions or state leakage - Debug Kafka message consumption issues **OPTIMIZE** - Improve E2E test performance: - Speed up slow E2E tests - Optimize Docker infrastructure startup - Replace fixed waits with smart polling - Reduce beforeEach cleanup time - Improve test parallelization where safe Keywords: e2e, end-to-end, integration test, e2e-spec.ts, test/e2e, Jest, supertest, NestJS, Kafka, Redpanda, PostgreSQL, MongoDB, Redis, docker-compose, GWT pattern, Given-When-Then, real infrastructure, test isolation, flaky test, MSW, nock, waitForMessages, fix e2e, debug e2e, run e2e, review e2e, optimize e2e, setup e2e