skills/aif-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 lee-to/ai-factory aif-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.
Read .ai-factory/skill-context/aif-dockerize/SKILL.md — MANDATORY if the file exists.
This file contains project-specific rules accumulated by /aif-evolve from patches,
codebase conventions, and tech-stack analysis. These rules are tailored to the current project.
How to apply skill-context rules:
Enforcement: After generating any output artifact, verify it against all skill-context rules. If any rule is violated — fix the output before presenting it to the user.
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 /aif-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
Additional focused references are available for production reverse-proxy and Compose permission edge cases:
references/ANGIE-ACME.md — read when generating or auditing Angie HTTPS/ACME configuration. Use it for built-in ACME setup, persistent certificate volumes, acme_client_path, resolver requirements, and avoiding unnecessary Certbot containers.references/COMPOSE-LIFECYCLE-HOOKS.md — read when a production Compose service runs non-root but needs named-volume ownership fixes or best-effort stop hooks. Use it for post_start/pre_stop syntax, Docker Compose 2.30.0+ requirements, and hook timing caveats.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): Use docker.angie.software/angie:<version>-alpine (Angie) or nginx:<version>-alpine (Nginx) — verify current version online. Mount config from docker/angie/. Sits on frontend network, proxies to app on backend network. In production: read_only, cap_add NET_BIND_SERVICE.
Environment variable strategy and service configuration patterns — Read references/COMPOSE-PATTERNS.md
Service inclusion is conditional — only add services that were detected in Step 2.5.
Development overrides: build.target: development, bind mount source code (.:/app), expose all ports, dev env vars, dev command override, mailpit service (profile: dev) if email detected. No database admin UIs — use native GUI clients via exposed DB port.
Hot-reload: If dev stage uses air (Go) or nodemon (Node.js), verify its config file exists and points to the correct entry point. Generate config if missing and entry point is non-standard.
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. Ask interactive questions about missing infrastructure (same as Step 1.3) before generating production files.
For detailed audit procedures, report format, fix flow, and enhance mode steps → read references/AUDIT-GUIDE.md
What to audit:
Present results as tables with ✅/❌/⚠️. Ask user: fix all, fix critical only, show details, or export report.
For detailed file organization (directory layout, file tables per mode, .env.example template, volume mount examples) → read references/FILE-ORGANIZATION.md
Dockerfile, compose.yml, compose.override.yml, compose.production.yml, .dockerignoredocker/: service configs (angie, postgres, php, redis) — only create what's neededdeploy/scripts/: production ops scripts (Step 8).env 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. Check categories: Container Isolation (read_only, no-new-privileges, cap_drop, non-root, tmpfs), Network & Ports (internal backend, no host networking, no Docker socket, no infra ports exposed), Resources (memory/CPU/PID limits), Secrets (.env not hardcoded, .gitignore, .env.example), Health & Logging (healthcheck, log rotation, restart policy), Images (version-pinned, minimal base).
Display as compact checklist with [x]/[ ] per item and a score. If any checks fail → offer to fix immediately.
Generate production deployment scripts in deploy/scripts/ from templates.
Read references/DEPLOY-SCRIPTS.md for script customization points and generation rules.
Templates: templates/deploy.sh, templates/update.sh, templates/logs.sh, templates/health-check.sh, templates/rollback.sh, templates/backup.sh
Display a summary of all created/updated files using the format from references/SUMMARY-FORMAT.md.
Suggest follow-up: /aif-build-automation for Docker targets, /aif-docs for documentation.
Dockerfile, compose*.yml, .dockerignore, docker/*, deploy/scripts/*, and related .env.example scaffolding when created by this skill).config.yaml.data-ai
Archive completed plans and roadmap milestones. Moves finished plans to the archive directory and optionally trims closed milestones from ROADMAP.md. Use when user says "archive plans", "clean up plans", "archive completed", or "trim roadmap".
tools
Set up agent context for a project. Analyzes tech stack, installs relevant skills from skills.sh, generates custom skills, and configures MCP servers. Use when starting new project, setting up AI context, or asking "set up project", "configure AI", "what skills do I need".
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 "/aif-implement" completes, or when user says "verify", "check work", "did we miss anything".
data-ai
Plan implementation for a feature or task. Two modes — fast (single quick plan) or full (richer plan with optional git branch/worktree flow). Use when user says "plan", "new feature", "start feature", "create tasks".