.claude/skills/dockerize/SKILL.md
Analyze project and generate Docker configuration: Dockerfile (multi-stage dev/prod), compose.yml, compose.override.yml (dev), compose.production.yml (hardened), and .dockerignore. Includes production security audit. Use when user says "dockerize", "add docker", "docker compose", "containerize", or "setup docker".
npx skillsauth add YaroslavKomarov/ShedulerBot ai-factory.dockerizeInstall 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.
Analyze a project and generate a complete, production-grade Docker setup: multi-stage Dockerfile, Docker Compose for development and production, .dockerignore, and a security audit of the result.
Three modes based on what exists:
| What exists | Mode | Action |
|-------------|------|--------|
| Nothing | generate | Create everything from scratch with interactive setup |
| Only local Docker (no production files) | enhance | Audit & improve local, then create production config |
| Full Docker setup (local + prod) | audit | Audit everything against checklist, fix gaps |
Read the project description if available:
Read .ai-factory/DESCRIPTION.md
Store project context for later steps. If absent, Step 2 detects everything.
Glob: Dockerfile, Dockerfile.*, docker-compose.yml, docker-compose.yaml, compose.yml, compose.yaml, compose.override.yml, compose.production.yml, .dockerignore, deploy/scripts/*.sh
Classify found files into categories:
HAS_DOCKERFILE: Dockerfile existsHAS_LOCAL_COMPOSE: compose.yml or docker-compose.yml existsHAS_DEV_OVERRIDE: compose.override.yml existsHAS_PROD_COMPOSE: compose.production.yml existsHAS_DOCKERIGNORE: .dockerignore existsHAS_DEPLOY_SCRIPTS: deploy/scripts/ existsIf $ARGUMENTS contains --audit → set MODE = "audit" regardless.
Path A: Nothing exists (!HAS_DOCKERFILE && !HAS_LOCAL_COMPOSE):
MODE = "generate"Path B: Only local Docker (HAS_LOCAL_COMPOSE && !HAS_PROD_COMPOSE):
MODE = "enhance"EXISTING_CONTENTPath C: Full setup exists (HAS_LOCAL_COMPOSE && HAS_PROD_COMPOSE):
MODE = "audit"EXISTING_CONTENTWhen creating from scratch, ask the user about their infrastructure needs:
AskUserQuestion: Which database does this project use?
Options:
1. PostgreSQL (Recommended)
2. MySQL / MariaDB
3. MongoDB
4. SQLite (no container needed)
5. None
AskUserQuestion: Does this project need a reverse proxy / web server?
Options:
1. Angie (Recommended) — Modern Nginx fork with enhanced features
2. Nginx
3. Traefik
4. None (app serves directly)
Note: Prefer Angie over Nginx. Angie is a drop-in Nginx replacement with better module support, dynamic configuration, and active development. See: https://en.angie.software/angie/docs/configuration/
AskUserQuestion: Which cache / message broker does this project need? (select all)
Options:
1. Redis
2. Memcached
3. RabbitMQ
4. None
Store choices in USER_INFRA_CHOICES:
database: postgres | mysql | mongodb | sqlite | nonereverse_proxy: angie | nginx | traefik | nonecache: redis | memcached | nonequeue: rabbitmq | noneRead all existing Docker files and store as EXISTING_CONTENT:
Scan the project thoroughly — every decision in the generated files depends on this profile.
| File | Language | Base Image |
|------|----------|------------|
| go.mod | Go | golang:<version>-alpine / distroless/static |
| package.json | Node.js | node:<version>-alpine |
| pyproject.toml / setup.py | Python | python:<version>-slim |
| composer.json | PHP | php:<version>-fpm-alpine |
| Cargo.toml | Rust | rust:<version>-slim / distroless |
<version> = read from project files (see Step 4.1). Never hardcode — always match what the project requires.
Read dependency files to detect the framework:
Node.js (package.json dependencies):
next → Next.js (port 3000, next dev / next start)nuxt → Nuxt (port 3000, nuxt dev / nuxt start)express → Express (port 3000, nodemon / node)fastify → Fastify (port 3000)@nestjs/core → NestJS (port 3000, nest start --watch / node dist/main)hono → Hono (port 3000)Python (pyproject.toml / requirements):
fastapi → FastAPI (port 8000, uvicorn --reload / uvicorn)django → Django (port 8000, manage.py runserver / gunicorn)flask → Flask (port 5000, flask run --debug / gunicorn)PHP (composer.json require):
laravel/framework → Laravel (port 8000, artisan serve / php-fpm)symfony/framework-bundle → Symfony (port 8000, symfony serve / php-fpm)Go (go.mod require):
gin-gonic/gin, labstack/echo, gofiber/fiber, go-chi/chi → (port 8080, air / compiled binary)Same detection as /ai-factory.build-automation Step 2.2.
Store: PACKAGE_MANAGER, LOCK_FILE.
Find the application entry point:
# Go
Glob: cmd/*/main.go, main.go
# Node.js
Read package.json → "main" or "scripts.start"
Glob: src/index.ts, src/index.js, src/main.ts, src/main.js, index.ts, index.js, server.ts, server.js
# Python
Glob: main.py, app.py, src/main.py, src/app.py
Read pyproject.toml → [project.scripts] or [tool.uvicorn]
# PHP
Glob: public/index.php, artisan, bin/console
Detect what services the app needs:
# Database
Grep: postgres|postgresql|pg_|mysql|mariadb|mongo|mongodb|sqlite
Glob: prisma/schema.prisma, drizzle.config.*, alembic/, migrations/
# Cache
Grep: redis|memcached|ioredis
# Queue
Grep: rabbitmq|amqp|bullmq|celery|sidekiq
# Reverse Proxy / Web Server
Grep: nginx|angie|proxy_pass|upstream
Glob: nginx.conf, nginx/, angie.conf, angie/
# PHP projects (Laravel, Symfony) always need a reverse proxy → default to Angie
# Search
Grep: elasticsearch|opensearch|meilisearch|typesense|algolia
# Object Storage
Grep: minio|s3|aws-sdk.*S3|boto3.*s3
# Email
Grep: nodemailer|sendgrid|mailgun|postmark|smtp|MAIL_HOST
For each detected dependency, record:
Merge with USER_INFRA_CHOICES (from Step 1.3 in Generate mode):
Reverse proxy preference: When a reverse proxy is needed, prefer Angie over Nginx. Angie is a fully compatible Nginx fork with active development, dynamic upstream management, and built-in Prometheus metrics. Reference: https://en.angie.software/angie/docs/configuration/
Check existing configs:
Grep: PORT|port|listen|EXPOSE
Read package.json → scripts.dev, scripts.start (look for --port)
# Node.js
Read package.json → scripts.build, check for dist/, build/, .next/, out/
Read tsconfig.json → outDir
# Go
Glob: cmd/*/main.go → binary name from directory
# Python
Check for pyproject.toml [build-system]
# PHP
Check for public/ directory (web root)
Glob: .env.example, .env.sample, .env.template
If found, read it to understand required environment variables. This drives env_file, environment: (computed values), and .env.example generation.
Build PROJECT_PROFILE:
language, language_versionframework, dev_command, prod_commandpackage_manager, lock_fileentry_point, build_output_dirport (primary app port)debug_port (language-specific debug port)services: list of infrastructure deps (postgres, redis, rabbitmq, etc.)has_build_step: booleanenv_vars: list from .env.exampleRead skills/dockerize/references/BEST-PRACTICES.md
Read skills/dockerize/references/SECURITY-CHECKLIST.md
Select the Dockerfile template matching the language:
| Language | Template |
|----------|----------|
| Go | templates/dockerfile-go |
| Node.js | templates/dockerfile-node |
| Python | templates/dockerfile-python |
| PHP | templates/dockerfile-php |
Read selected template and the compose templates:
Read skills/dockerize/templates/dockerfile-<language>
Read skills/dockerize/templates/compose-base.yml
Read skills/dockerize/templates/compose-override-dev.yml
Read skills/dockerize/templates/compose-production.yml
Read skills/dockerize/templates/dockerignore
Generate files customized from the project profile and templates.
Using the language-specific template as a base:
Customize:
go directive in go.mod → e.g. go 1.24 → golang:1.24-alpineengines.node in package.json, .nvmrc, or .node-version → e.g. node:22-alpinerequires-python in pyproject.toml or .python-version → e.g. python:3.13-slimrequire.php in composer.json → e.g. php:8.4-fpm-alpinerust-version in Cargo.toml or rust-toolchain.toml → e.g. rust:1.82-slimentry_pointStages:
deps — install production dependencies onlybuilder — install all dependencies + builddevelopment — full dev environment with hot reload, debug portproduction — minimal image, non-root user, only runtime artifactsVerify infrastructure image versions online:
For infrastructure images (PostgreSQL, Redis, Angie, Nginx, etc.) — the version is NOT in project files. Before generating compose.yml, use WebSearch to check the current stable version of each infrastructure image:
<service> docker official image latest version (e.g. angie docker image latest version)major.minor tag, never :latestdocker.angie.software/angie:1.11-alpine, postgres:17-alpine, redis:7-alpineThis prevents generating non-existent image tags that would break docker compose pull.
The shared configuration:
name: ${COMPOSE_PROJECT_NAME} — project name from .env, NOT from folder nameapp service with build.target: production, healthcheck, depends_on with service_healthyPROJECT_PROFILE.services + USER_INFRA_CHOICES:
Reverse proxy (Angie/Nginx):
docker.angie.software/angie:<version>-alpine (Angie) or nginx:<version>-alpine (Nginx) — verify current version online before using./docker/angie/angie.conf:/etc/angie/angie.conf:roCMD wget --spider -q http://localhost/health || exit 1frontend network, proxies to app on backend networkfrontend and backend networksEnvironment variable strategy — env_file over environment:
Use env_file: .env on app service. Do NOT list every app variable in environment:.
Only use environment: for:
DATABASE_URL: postgres://${DB_USER}:${DB_PASSWORD}@db:5432/${DB_NAME}POSTGRES_DB, POSTGRES_USER, POSTGRES_PASSWORD on db serviceEverything else (API keys, feature flags, app settings) — the app reads from .env directly via env_file:.
# CORRECT
services:
app:
env_file: .env
environment:
DATABASE_URL: postgres://${DB_USER}:${DB_PASSWORD}@db:5432/${DB_NAME}
# WRONG — duplicating .env in compose, maintenance burden
services:
app:
environment:
OPENAI_API_KEY: ${OPENAI_API_KEY:-}
ADMIN_PASSWORD: ${ADMIN_PASSWORD:-}
TOKEN_TTL_DAYS: ${TOKEN_TTL_DAYS:-7}
# ...20 more lines of the same
Service inclusion is conditional — only add services that were detected in Step 2.5.
Development overrides:
build.target: development.:/app) with anonymous volume for depsNODE_ENV=development / LOG_LEVEL=debug etc.mailpit → if email sending detected (profile: dev)Hot-reload tool config check:
If the dev stage uses a hot-reload tool, verify its config file exists and points to the correct entry point. Many tools assume the main file is in the project root — if it's not (e.g. ./cmd/server/main.go), the tool will fail without a config.
| Stack | Tool | Config | Key setting |
|-------|------|--------|-------------|
| Go | air | .air.toml | build.cmd → path to main package |
| Node.js | nodemon | nodemon.json | exec / watch paths |
If the config file doesn't exist and the entry point is non-standard, generate it alongside the Docker files. If the config already exists, do not overwrite it.
Production hardening overlay:
build:)read_only: true on all servicessecurity_opt: [no-new-privileges:true]cap_drop: [ALL] with selective cap_add per serviceuser: "1001:1001"tmpfs for /tmp with noexec,nosuid,size=100mmax-size: 20m, max-file: 5)restart: unless-stoppedbackend network with internal: true.env file (gitignored) — NOT hardcoded in composex-logging, x-security) to reduce duplicationports: on infrastructure services (DB, Redis, RabbitMQ) — they communicate via Docker network only80/443 to the host127.0.0.1:5432:5432Use the template as base, add language-specific exclusions:
bin/, *.exenode_modules/, .next/, out/__pycache__/, .venv/, *.pyc, .mypy_cache/vendor/, storage/, bootstrap/cache/Verify generated content before passing to Step 6:
Correctness:
depends_on with condition: service_healthy.git, dependencies, .env*, Docker filesOver-engineering check (read references/SECURITY-CHECKLIST.md → "Over-Engineering Checklist"):
Remove anything that fails the over-engineering check before writing.
When MODE = "enhance" or MODE = "audit", analyze EXISTING_CONTENT against the security checklist and best practices.
Enhance mode (MODE = "enhance"): Local Docker exists but no production config. After auditing local files, create production configuration (compose.production.yml, deploy scripts, security hardening). Ask interactive questions about missing infrastructure (same as Step 1.3) before generating production files.
Read each section from references/SECURITY-CHECKLIST.md → "Dockerfile Security" and check:
:latest)Read each section from references/SECURITY-CHECKLIST.md → "Compose Security" and check:
For each service:
read_only: true?security_opt: [no-new-privileges:true]?cap_drop: [ALL]?user: specified?tmpfs for temp directories?Network security:
internal: true?network_mode: host?Secrets:
.env (not hardcoded in compose)?.env in .gitignore?.env.example exists with placeholder values?Compare existing compose against PROJECT_PROFILE:
## Docker Security Audit
### Dockerfile
| Check | Status | Detail |
|-------|--------|--------|
| Pinned base image | ✅ | node:22.5-alpine |
| Multi-stage build | ✅ | 3 stages |
| Non-root user | ❌ | Running as root in final stage |
| No secrets in ENV | ✅ | |
| .dockerignore | ⚠️ | Missing: .env*, docker-compose* |
| Healthcheck | ❌ | No HEALTHCHECK instruction |
### compose.yml
| Service | read_only | no-new-privs | cap_drop | resources | healthcheck | logging |
|---------|-----------|-------------|----------|-----------|-------------|---------|
| app | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ |
| db | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ |
| redis | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
### Missing Infrastructure
- Redis detected in code but not in compose
- RabbitMQ connection string found but no service defined
### Recommendations
1. CRITICAL: Add non-root user to Dockerfile
2. CRITICAL: Create compose.production.yml with security hardening
3. HIGH: Add resource limits to all services
4. HIGH: Add log rotation to all services
5. MEDIUM: Add healthcheck to redis service
6. LOW: Update .dockerignore to exclude .env files
AskUserQuestion: Audit found issues. What should we do?
Options:
1. Fix all — Apply all recommendations
2. Fix critical only — Fix security issues, skip improvements
3. Show details — Explain each issue before deciding
4. Export report — Save audit report to .ai-factory/docker-audit.md
If fixing:
Only for MODE = "enhance" (local Docker exists, no production config):
After auditing and fixing local compose, proceed to generate missing production files:
Ask infrastructure questions (same as Step 1.3) for any services not yet in compose:
Generate missing files:
compose.production.yml → hardened overlay (Step 4.4).dockerignore → if missing (Step 4.5).env.example → if missing (Step 6.3)Improve existing files:
COMPOSE_PROJECT_NAME to compose.yml if missingdepends_on with condition: service_healthyRoot directory — only files Docker expects by convention:
Dockerfile — CI/CD, Docker Hub, GitHub Actions look for it in rootcompose.yml, compose.override.yml, compose.production.yml — docker compose looks in root.dockerignore — must be in build context rootdocker/ directory — all service configs and supporting files:
docker/
├── angie/ # Reverse proxy (if used)
│ ├── angie.conf
│ └── conf.d/
│ └── default.conf
├── postgres/ # DB init scripts (if needed)
│ └── init.sql
├── php/ # PHP-FPM config (if PHP project)
│ ├── php.ini
│ └── php-fpm.conf
└── redis/ # Custom Redis config (if needed)
└── redis.conf
deploy/ directory — production ops scripts:
deploy/
└── scripts/
├── deploy.sh
├── update.sh
├── logs.sh
├── health-check.sh
├── rollback.sh
└── backup.sh
Rule: Only create directories that are needed. If no reverse proxy → no docker/angie/. If no custom DB init → no docker/postgres/.
Always created (root):
| File | Purpose |
|------|---------|
| Dockerfile | Multi-stage (dev + prod) |
| compose.yml | Base configuration with COMPOSE_PROJECT_NAME |
| compose.override.yml | Development overrides |
| compose.production.yml | Production hardened |
| .dockerignore | Build context exclusions |
Conditionally created (docker/):
| Directory | When |
|-----------|------|
| docker/angie/ | Reverse proxy selected (Angie/Nginx) |
| docker/postgres/ | Custom init scripts needed |
| docker/php/ | PHP project (php.ini, php-fpm.conf) |
| docker/redis/ | Custom Redis config needed |
Always created:
| Directory | Purpose |
|-----------|---------|
| deploy/scripts/ | Production ops scripts (Step 8) |
Update compose volumes to reference docker/ paths:
# Example: Angie config mount
volumes:
- ./docker/angie/angie.conf:/etc/angie/angie.conf:ro
- ./docker/angie/conf.d:/etc/angie/conf.d:ro
Only write files that were changed or created. Don't overwrite files that passed audit. Respect existing file structure — if project already uses a different layout (e.g. nginx/ instead of docker/nginx/), follow their convention.
If .env.example doesn't exist, generate one. Single file with sections — no separate .env.prod.example. Production-only vars are commented out.
Build from: compose variables + detected app env vars from .env.example/code.
# === Project ===
COMPOSE_PROJECT_NAME=myapp
# === Database ===
DB_NAME=mydb
DB_USER=app
DB_PASSWORD=changeme
POSTGRES_VERSION=17
# === Application ===
LOG_LEVEL=debug # prod: warn
# (add project-specific vars detected in Step 2.8)
# === Production (uncomment for deploy) ===
# DOCKER_REGISTRY=ghcr.io
# DOCKER_IMAGE=myapp
# VERSION=latest
# ALLOWED_ORIGINS=https://myapp.com
# TRUSTED_PROXIES=172.16.0.0/12
Also ensure .env is in .gitignore.
Regardless of mode, run the production security checklist on the final compose.production.yml.
Read references/SECURITY-CHECKLIST.md and verify every item on the generated/existing production file.
Display a compact checklist result:
## Production Security Checklist
### Container Isolation
- [x] read_only filesystem on all services
- [x] no-new-privileges on all services
- [x] cap_drop ALL on all services
- [x] Non-root user on all services
- [x] tmpfs for temp directories
### Network & Ports
- [x] Backend network internal (no internet)
- [x] No host networking
- [x] No Docker socket mounted
- [x] No ports exposed on infrastructure services (DB, Redis)
- [x] Only proxy/app exposes 80/443
### Resources
- [x] Memory limits on all services
- [x] CPU limits on all services
- [x] PID limits on all services
### Secrets
- [x] Sensitive values in .env file (not hardcoded in compose)
- [x] .env file in .gitignore
- [x] .env.example exists (without real values)
### Health & Logging
- [x] Healthcheck on every service
- [x] Log rotation on every service
- [x] restart: unless-stopped on all services
### Images
- [x] All images version-pinned
- [x] Minimal base images used
- [ ] Image vulnerability scanning (recommend: add to CI)
Score: 20/21 checks passed
If any checks fail → offer to fix immediately (same as Step 5.5).
Generate production deployment scripts in deploy/scripts/. These scripts assume compose.yml + compose.production.yml and provide a complete ops toolkit.
Read skills/dockerize/templates/deploy.sh
Read skills/dockerize/templates/update.sh
Read skills/dockerize/templates/logs.sh
Read skills/dockerize/templates/health-check.sh
Read skills/dockerize/templates/rollback.sh
Read skills/dockerize/templates/backup.sh
Customize each template based on PROJECT_PROFILE:
| Script | Purpose | Customization |
|--------|---------|---------------|
| deploy/scripts/deploy.sh | Initial production deployment | Pre-flight checks, build, start, health verify |
| deploy/scripts/update.sh | Zero-downtime rolling update | Pre-backup, pull, build, recreate app, health check |
| deploy/scripts/logs.sh | Log aggregation utility | Service names from compose |
| deploy/scripts/health-check.sh | Full health diagnostics | App port, health endpoints |
| deploy/scripts/rollback.sh | Version rollback | Git-based version detection |
| deploy/scripts/backup.sh | Database backup with retention | DB_USER, DB_NAME from .env |
Customization points for all scripts:
COMPOSE_FILE / COMPOSE_PROD paths (relative from deploy/scripts/)PROJECT_PROFILE.port.env.examplecompose.ymlAll scripts must:
set -euo pipefaillog_info, log_success, log_error)PROJECT_ROOT relative to script locationdocker compose -f compose.yml -f compose.production.yml patternWrite deploy/scripts/deploy.sh
Write deploy/scripts/update.sh
Write deploy/scripts/logs.sh
Write deploy/scripts/health-check.sh
Write deploy/scripts/rollback.sh
Write deploy/scripts/backup.sh
Bash: chmod +x deploy/scripts/*.sh
If MODE = "audit" and deploy scripts already exist:
## Docker Setup Complete
### Files Created/Updated
- Dockerfile (multi-stage: development + production)
- compose.yml (app + postgres + redis, COMPOSE_PROJECT_NAME from .env)
- compose.override.yml (dev: hot reload, debug ports, mailpit)
- compose.production.yml (hardened: read-only, non-root, resource limits, no infra ports)
- .dockerignore (38 exclusion rules)
- .env.example (with COMPOSE_PROJECT_NAME, DB credentials, app config)
- docker/angie/ (reverse proxy config, if needed)
- deploy/scripts/ (deploy, update, logs, health-check, rollback, backup)
### Quick Start
# Development
docker compose up
# Development with email testing
docker compose --profile dev up
# Production (locally)
docker compose -f compose.yml -f compose.production.yml up -d
# Build production image
docker build --target production -t myapp:latest .
### Services
| Service | Port (dev) | Port (prod) | Image |
|---------|------------|-------------|-------|
| app | 3000, 9229 | — | built locally |
| postgres | 5432 | — | postgres:17-alpine |
| redis | 6379 | — | redis:7-alpine |
| mailpit | 8025, 1025 | — | axllent/mailpit |
AskUserQuestion: Docker setup complete. What's next?
Options:
1. Build automation — Run /ai-factory.build-automation to add Docker targets to Makefile/Taskfile
2. Update docs — Run /ai-factory.docs to document the Docker setup
3. Both — Build automation first, then docs
4. Done — Skip follow-ups
If build automation → suggest invoking /ai-factory.build-automation
If docs → suggest invoking /ai-factory.docs
If both → suggest build-automation first, then docs
development
Verify completed implementation against the plan. Checks that all tasks were fully implemented, nothing was forgotten, code compiles, tests pass, and quality standards are met. Use after "/ai-factory.implement" completes, or when user says "verify", "check work", "did we miss anything".
data-ai
Create a step-by-step implementation plan for a feature or task. Breaks down work into actionable tasks tracked via the task system. Use when user says "plan", "create tasks", "break down", or "make a plan for".
tools
# Supabase TypeScript Patterns Patterns for using Supabase with TypeScript in this project. Uses **service role key** (server-side only). Tables are prefixed `sch_`. ## Client Setup ```typescript // src/db/client.ts import { createClient } from "@supabase/supabase-js"; import type { Database } from "./types"; // generated types export const supabase = createClient<Database>( process.env.SUPABASE_URL!, process.env.SUPABASE_SERVICE_ROLE_KEY!, // server-side only, bypasses RLS { auth:
development
Generate professional Agent Skills for Claude Code and other AI agents. Creates complete skill packages with SKILL.md, references, scripts, and templates. Use when creating new skills, generating custom slash commands, or building reusable AI capabilities. Validates against Agent Skills specification.