From co2-skills
Generate a Dockerfile and Kubernetes manifests for an application targeting a single environment. Supports Spring Boot (Java), Laravel (PHP), and Node.js application stacks. Auto-detects the stack from project files (pom.xml, composer.json, package.json), reads CLAUDE.md dependencies, SPECIFICATION.md tech stack, and the application's externalized environment variables. Generates a Dockerfile in the application root folder and Kubernetes manifest YAML files directly in `<app_folder>/k8s/` (no per-environment subfolders — the k8s/ folder is gitignored, each machine maintains its own copy). Standardized input: application name (mandatory), environment (optional). Use this skill whenever the user asks to create deployment artifacts, Dockerfiles, Kubernetes manifests, or containerize an application. Also trigger when the user says things like "deploy this app", "containerize this", "create a Dockerfile", "generate k8s manifests", or any request for deployment-related artifacts.
How this skill is triggered — by the user, by Claude, or both
Slash command
/co2-skills:depgen-k8sclaude-sonnet-4-6The summary Claude sees in its skill listing — used to decide when to auto-load this skill
This skill generates deployment artifacts for a custom application:
This skill generates deployment artifacts for a custom application:
k8s/ folder for a single target environment. Since the k8s/ folder is gitignored,
each machine maintains its own independent copy of the manifests with environment-specific
ConfigMap/Secret values (hostnames, credentials, resource limits).Note: Only custom applications (from # Custom Applications in CLAUDE.md) are processed.
3rd party supporting applications and external services are NOT containerized by this skill.
/depgen-k8s <application> [environment]
| Argument | Required | Example | Description |
|---|---|---|---|
<application> | Yes | hub_middleware | Application name — must be a custom application from CLAUDE.md |
<environment> | No | home_server | Target environment name — must match a Kubernetes environment in CLAUDE.md |
If <environment> is omitted:
The environment name is matched case-insensitively against the environment headings in CLAUDE.md, accepting snake_case, kebab-case, or title-case input (e.g., home_server, home-server, Home Server).
No version: or module: arguments — deployment is application-level.
The application name is matched against root-level application folders:
<number>_ prefix from folder names (e.g., 1_hub_middleware → hub_middleware)# Custom Applications in CLAUDE.md — reject if it is a 3rd party application or external service| File | Resolved Path |
|---|---|
| CLAUDE.md | Project root CLAUDE.md |
| ENVIRONMENT.md | Project root ENVIRONMENT.md |
| SPECIFICATION.md | <app_folder>/context/specification/SPECIFICATION.md |
| Source code | <app_folder>/ (pom.xml, composer.json, package.json, etc.) |
| Application config | Stack-dependent (see detection) |
| K8s manifests output | <app_folder>/k8s/ (gitignored — each machine maintains its own copy) |
Before generating deployment artifacts, check PRD.md for the following extended sections:
If PRD.md contains an # Architecture Principle section, extract patterns that affect deployment:
| Pattern | Deployment Impact |
|---|---|
| "Stateless" | Deployment uses RollingUpdate strategy without sticky sessions; no PVC needed for session storage |
| "Container based deployment" | Validates this skill's applicability |
| "Scale out" / "horizontally scalable" | Generate HorizontalPodAutoscaler (HPA) manifest targeting CPU 70% |
| Specific resource constraints (e.g., "max 256Mi per pod") | Use as default resource limits in K8s manifests |
| "Message driven" / "message queue" | Readiness probe should verify message queue connectivity |
If absent, use defaults from SPECIFICATION.md and CLAUDE.md (existing behavior).
If PRD.md contains a # High Level Process Flow section:
Depends on list, log a warningIf absent, derive dependencies from CLAUDE.md only (existing behavior).
Before running this skill, the following must exist:
specgen-* skill)${ENV_VAR:default} in Spring Boot, env('VAR') in Laravel, process.env.VAR in Node.js)If any are missing, stop and inform the user.
Resolve application folder from the provided name
Verify the application is a custom application in CLAUDE.md (not 3rd party or external service)
Verify source code exists (at least one of: pom.xml, composer.json, package.json)
Verify <app_folder>/context/specification/SPECIFICATION.md exists
Read CLAUDE.md (already in context) for project-level information
Detect environments from CLAUDE.md's # Environment section:
## <Environment Name> heading under # Environment is an environmentDomain field (e.g., localhost, home.server)Deployment Type field (e.g., Manual, Kubernetes)IP field (from SSH Configuration or direct IP field) if present — needed for hostAliases generation in Phase 3# Environment section."Select target environment — filter to Kubernetes environments and select one:
Deployment Type: Kubernetes.Deployment Type: Kubernetes, stop with error: "No Kubernetes environments found in CLAUDE.md. This skill only generates K8s manifests for environments with Deployment Type: Kubernetes."<environment> argument, match it against the Kubernetes environments (case-insensitively, accepting snake_case, kebab-case, or title-case).
Check the application root for build files:
| File Found | Stack | Build Tool |
|---|---|---|
pom.xml | spring-boot | Maven |
composer.json | laravel | Composer |
package.json (no pom.xml or composer.json) | nodejs | npm / pnpm |
If none found, stop with error: "Cannot determine application stack."
If multiple found (e.g., pom.xml + package.json), prioritize: pom.xml > composer.json > package.json
(the package.json is likely for frontend or E2E tests, not the main app).
If spring-boot:
pom.xml:
<java.version> → JDK version for base image<artifactId> → image name<version> → image tagfrontend-maven-plugin presence → has frontend build stepjte-maven-plugin presence → has JTE precompilationspring-boot-maven-plugin → executable JAR packagingsrc/main/resources/application.yml:
${ENV_VAR:default} patterns via regex \$\{([^:}]+)(?::([^}]*))?\}server.port default → exposed container port.env (if exists) → local dev values for referenceIf laravel:
composer.json:
require.php → PHP version for base imagerequire.laravel/framework → Laravel versionname → image name.env.example or scan config/*.php:
env('VAR', 'default') patterns via regex env\('([^']+)'(?:,\s*'?([^')*]*)'?)?\)APP_PORT or default 8000 → exposed container portvite.config.js or webpack.mix.js → has frontend build steppackage.json in app root → Node.js needed for frontend buildIf nodejs:
package.json:
engines.node → Node.js version for base imagename → image nameversion → image tagscripts.build → build command (e.g., tsc, tsup, vite build, next build)scripts.start → start command.env for process.env.VAR patternstsconfig.json → TypeScript build stepFrom CLAUDE.md → extract the "Depends on" list for the target application:
| Dependency Pattern | External Service |
|---|---|
| References MongoDB | MongoDB required |
| References MySQL / HC Database / SC Database | MySQL required |
| References PostgreSQL | PostgreSQL required |
| References Hub Single Sign On / Keycloak | Keycloak required |
| References RabbitMQ / Message Queue | RabbitMQ required |
| References Redis / Hub Cache | Redis required |
| References SMTP / Mailcatcher | SMTP service required |
| References Meilisearch / Hub Search Engine | Meilisearch required |
From SPECIFICATION.md → extract:
For each detected environment variable, classify as ConfigMap or Secret:
Secret (sensitive — stored in K8s Secret):
PASSWORD, SECRET, KEY, TOKEN, CREDENTIALADMIN_USERNAME (admin-level credentials)ConfigMap (non-sensitive — stored in K8s ConfigMap):
| Stack | Default Health Endpoint | Condition |
|---|---|---|
| Spring Boot | /actuator/health | If spring-boot-starter-actuator in pom.xml |
| Spring Boot | / (HTTP 200/302 check) | If no actuator |
| Laravel | /health or / | Custom route or root |
| Node.js | /health or / | Custom route or root |
Check if a health endpoint is explicitly defined in the source code. If not, use the default for the stack.
Before generating, present findings to the user for confirmation:
Application Stack Detection:
- Stack: Spring Boot 3.5.7 (Java 21)
- Artifact: hub-middleware:1.0.3
- Frontend Build: Yes (Vite via frontend-maven-plugin)
- JTE Precompile: Yes (jte-maven-plugin)
- Exposed Port: 8080
- Health Check: /actuator/health (actuator present)
External Services:
- MongoDB 7.0.6 (Hub Core Database)
- Keycloak 26.5.3 (Hub Single Sign On)
- RabbitMQ 3.11.9 (HC + SC Adapter Message Queues)
- Mailcatcher 0.8.1 (SMTP)
Environment Variables: 25 detected
- ConfigMap: 18 (hostnames, ports, log levels, queue names, flags)
- Secret: 7 (passwords, admin credentials)
If the user disagrees, allow overrides before proceeding.
<app_folder>/Dockerfile already exists# CUSTOM: comments — these are manual overrides
by the user/DevOps that should not be replacedreferences/dockerfile-<stack>.md<app_folder>/DockerfileThe Dockerfile MUST:
latest).env, context/, e2e/, or test files into the imageHEALTHCHECK instruction if a health endpoint is availableLABEL version="{version}" instruction using the application version extracted
from pom.xml (<version>), composer.json (version), or package.json (version)ARG APP_VERSION={version} that can be overridden at build time
via docker build --build-arg APP_VERSION=1.0.3Read references/dockerfile-spring-boot.md, references/dockerfile-laravel.md, or
references/dockerfile-nodejs.md for the complete Dockerfile template per stack.
Kubernetes manifests are stored directly in <app_folder>/k8s/ (no per-environment
subfolders). Since the k8s/ folder is gitignored, each machine maintains its own
independent copy with values specific to the target environment selected in Phase 0.
<app_folder>/k8s/ exists. If not, create it.Example folder structure:
hub_middleware/
Dockerfile
k8s/
namespace.yaml
configmap.yaml
secret.yaml
deployment.yaml
service.yaml
ingress.yaml (optional)
Generate individual YAML files inside <app_folder>/k8s/ using values from the target
environment selected in Phase 0:
namespace.yaml — Namespace resource (use project code from CLAUDE.md in lowercase,
e.g., urp). Shared across all applications — identical content for every environment.configmap.yaml — Non-sensitive environment variables (from Step 4 classification).
Values may differ per environment — use defaults from .env for local development;
use TODO placeholders for other environments where values are not known.secret.yaml — Sensitive environment variables (from Step 4 classification),
base64-encoded. Values use TODO placeholders for all non-local environments.deployment.yaml — Container spec with:
image: hub-middleware:1.0.3), NOT latestenvFrom referencing ConfigMap and SecretsecurityContexthostAliases (conditional): If the environment's Domain from CLAUDE.md is referenced
in ConfigMap values AND is not localhost, add hostAliases mapping the Domain to
the environment's IP address. This is needed because K8s pod DNS cannot resolve custom
domains defined only in the host machine's /etc/hosts. See references/k8s-patterns.md.service.yaml — ClusterIP service exposing the application portingress.yaml (optional) — if the application is a web application or API with
external access. Only generate if the environment's CLAUDE.md description suggests
external access (e.g., mentions domain, IP, or ingress).Read references/k8s-patterns.md for the complete manifest templates per resource type.
When generating manifests for the target environment:
ENVIRONMENT.md from the project root. If ENVIRONMENT.md
contains environment-specific credentials (organized by environment), use the matching
values for the target environment. If values are not found, use TODO as placeholder.TODO if not specified.For the <app_folder>/k8s/ folder:
If a manifest file does not exist (CREATE mode):
<app_folder>/k8s/<resource>.yamlIf it already exists (UPDATE mode):
# CUSTOM: commentsAll manifests use the same namespace (derived from the project code in CLAUDE.md's
# Project Detail → Project Code in lowercase, e.g., urp).
Include a comment block at the top of deployment.yaml:
# Application: <Application Name>
# Environment: <Target Environment Name>
# Build: docker build -t <image-name>:<version> <app_folder>/
# Tag: docker tag <image-name>:<version> <image-name>:latest
# Apply: kubectl apply -f <app_folder>/k8s/
---
.gitignoreThe k8s/ folder contains environment-specific manifests with ConfigMap values,
base64-encoded Secrets, hostnames, and credentials that MUST NOT be committed to
version control. After generating or updating the K8s manifests, ensure the
application's .gitignore excludes the k8s/ folder:
<app_folder>/.gitignore. If it does not exist, create it.k8s/ (or an equivalent pattern like k8s/**) is already listed.
# Kubernetes manifests — environment-specific configs and secrets
k8s/
.gitignore.These constraints are non-negotiable:
Custom applications only — This skill only processes applications listed under
# Custom Applications in CLAUDE.md. 3rd party applications and external services are
NOT containerized by this skill.
Stack-agnostic output — The skill must work for Spring Boot, Laravel, and Node.js applications. All stack-specific logic is gated behind the detection in Phase 1.
Real values from context — Every manifest and Dockerfile must use the actual
application name, port, environment variables, and dependency versions from the project's
context files. No placeholder <TODO> values except for legitimately unknown production
values (registry URL, domain name).
ConfigMap/Secret separation — Sensitive values must NEVER appear in ConfigMaps. The classification logic in Phase 1 Step 4 must be applied consistently.
Non-root containers — All Dockerfiles must run the application as a non-root user.
Multi-stage builds — All Dockerfiles must use multi-stage builds to minimize the runtime image size. Build tools (Maven, Composer, npm dev dependencies) must NOT be in the final image.
No .env in containers — The .env file is for local development only. Container
environments receive configuration via K8s ConfigMap + Secret injection.
Flat K8s manifest folder — Kubernetes manifests live directly in <app_folder>/k8s/
with separate YAML files per resource type (no per-environment subfolders). The k8s/
folder is gitignored — each machine maintains its own copy with values specific to
the target environment. Run the skill once per environment on the target machine.
Idempotent — supports both create and update — If Dockerfile or manifest already
exists, read it first, detect what changed, and update only the affected parts. Preserve
any manual customizations marked with # CUSTOM:. If the file does not exist, create
from scratch. Always log what was created or changed.
Single-pass execution — This skill completes in one pass (Dockerfile + manifest). No Ralph Loop needed.
Production-safe defaults — Dockerfile and K8s manifests must default to production-safe values. Dev-specific settings (JTE dev mode, DevTools, debug logging) must be OFF by default in the container, overridden only via explicit environment variables.
Versioned image tags — Kubernetes Deployment manifests must reference versioned
image tags (e.g., hub-middleware:1.0.3), never latest.
Provides behavioral guidelines to reduce common LLM coding mistakes, focusing on simplicity, surgical changes, assumption surfacing, and verifiable success criteria.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
npx claudepluginhub rashidee/co2-skills --plugin co2-skills