K8s YAML Generator
Comprehensive toolkit for generating, validating, managing Kubernetes YAML resources. Use this skill when creating Kubernetes manifests (Deployments, Services, ConfigMaps, StatefulSets, etc.), working with Custom Resource Definitions (CRDs), generating production-ready K8s configurations.
npx skills add github:pantheon-org/tekhne --skill infrastructure--k8s--yaml-generatorMindset
Mental Model: Kubernetes YAML generation is about translating application requirements into declarative infrastructure. Think in terms of desired state, not imperative commands. A manifest that merely “applies without error” is not the bar — it must also survive a node restart, a rolling update, and a resource crunch without silently degrading, so generation and validation are never separable steps. The recurring pitfall is generating a manifest that validates cleanly today but hits a gotcha under load six months later, because a default (an inferred imagePullPolicy, an unset resource limit) was never made explicit.
Decision Framework:
- Start with the workload (Pod, Deployment, StatefulSet)—this defines what runs
- Add access layer (Service, Ingress)—this defines how it’s accessed
- Configure runtime (ConfigMap, Secret)—this defines how it behaves
- Set constraints (ResourceQuota, LimitRange, NetworkPolicy)—this defines boundaries
When to use this skill:
- Creating new Kubernetes resources from scratch
- Converting Docker Compose or other formats to K8s manifests
- Generating CRD instances (ArgoCD, Istio, Cert-Manager, etc.)
- Building templates for Helm charts or Kustomize bases
- Scaffolding multi-resource applications
When NOT to use this skill:
- The task is validating an already-written manifest rather than producing a new one — use
k8s-yaml-validatordirectly instead. - The target is a Helm chart’s templating logic itself (
{{ }}expressions,_helpers.tpl) rather than the rendered resource — that is chart authoring, not resource generation. - A near-identical resource already exists in the repository — copy and adapt it so labels, annotations, and conventions stay consistent, rather than generating a fresh one that drifts from house style.
Generation philosophy: Generate correct, complete, validated YAML on first pass. Never output YAML that hasn’t been validated — this is a mandatory, non-negotiable gate, not a suggestion.
Core Workflow
1. Understand Requirements
Gather: resource type, target K8s version, app requirements (replicas, ports, volumes), namespace/labels/annotations, and CRD specifics (kind, apiVersion, version).
2. Fetch CRD Documentation (if needed)
Look up the CRD specification in the project’s own documentation. If the CRD is already installed in a reachable cluster, kubectl explain <kind>.spec shows the fields for the installed version. A documentation search looks like:
"<project-name> <CRD-kind> <version> specification"
# e.g. "argo-cd Application v1alpha1 specification"
# e.g. "istio VirtualService v1beta1 specification"
# e.g. "cert-manager Certificate v1 specification"
The query should include:
- Project/operator name (e.g., “argo-cd”, “istio”, “cert-manager”)
- CRD kind (e.g., “Application”, “Certificate”, “VirtualService”)
- API version (e.g., “v1alpha1”, “v1”, “v1beta1”)
- Context about what you need (e.g., “spec fields”, “examples”, “configuration”)
If documentation is insufficient, fall back to web search:
WebSearch: "<CRD-name> <version> spec documentation"
# e.g. "ArgoCD Application v1alpha1 spec documentation"
Always include the version in the query for compatibility.
3. Generate YAML Resource
Recommended labels (use consistently across all resources):
labels:
app.kubernetes.io/name: myapp
app.kubernetes.io/instance: myapp-prod
app.kubernetes.io/version: "1.0.0"
app.kubernetes.io/component: backend
app.kubernetes.io/part-of: myplatform
app.kubernetes.io/managed-by: claude-code
Best practices checklist:
- Explicit, non-deprecated API versions
- Resource requests/limits on all containers
- Liveness and readiness probes
- Non-root
securityContext; Pod Security Standards - Secrets in Secret objects (not ConfigMaps)
- Namespace specified on all namespace-scoped resources
- For CRDs: include all required fields; add comments on complex configs
Deployment template:
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
namespace: default
labels:
app.kubernetes.io/name: myapp
app.kubernetes.io/instance: myapp-prod
spec:
replicas: 3
selector:
matchLabels:
app.kubernetes.io/name: myapp
app.kubernetes.io/instance: myapp-prod
template:
metadata:
labels:
app.kubernetes.io/name: myapp
app.kubernetes.io/instance: myapp-prod
spec:
containers:
- name: myapp
image: myapp:1.0.0
ports:
- containerPort: 8080
resources:
requests: { memory: "64Mi", cpu: "250m" }
limits: { memory: "128Mi", cpu: "500m" }
livenessProbe:
httpGet: { path: /health, port: 8080 }
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet: { path: /ready, port: 8080 }
initialDelaySeconds: 5
periodSeconds: 5
Service template:
apiVersion: v1
kind: Service
metadata:
name: myapp-service
namespace: default
spec:
type: ClusterIP # or LoadBalancer, NodePort
selector:
app.kubernetes.io/name: myapp
app.kubernetes.io/instance: myapp-prod
ports:
- protocol: TCP
port: 80
targetPort: 8080
name: http
ConfigMap template:
apiVersion: v1
kind: ConfigMap
metadata:
name: myapp-config
namespace: default
data:
app.properties: |
key1=value1
config.json: |
{ "setting": "value" }
4. Validate Generated YAML
CRITICAL: Always validate using the k8s-yaml-validator workflow immediately after generation.
See: yaml-validator/SKILL.md (in this tile)
The validator runs yamllint (syntax), kubeconform (schema/API compliance), best-practice checks, and optional cluster dry-run. Address all reported errors—fix, re-validate, repeat until clean.
5. Deliver the Resource
Present the validated YAML with a brief summary, key configuration choices, and next steps:
kubectl apply -f ./<filename>.yaml
kubectl get <resource-type> <name> -n <namespace>
kubectl describe <resource-type> <name> -n <namespace>
Common CRD Examples
ArgoCD Application
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: myapp
namespace: argocd
labels:
app.kubernetes.io/managed-by: argocd
spec:
project: default
source:
repoURL: https://github.com/org/repo
targetRevision: HEAD
path: manifests
destination:
server: https://kubernetes.default.svc
namespace: myapp
syncPolicy:
automated:
prune: true
selfHeal: true
Istio VirtualService
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: myapp
spec:
hosts:
- myapp.example.com
gateways:
- myapp-gateway
http:
- route:
- destination:
host: myapp-service
port:
number: 8080
Cert-Manager Certificate
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: myapp-tls
namespace: default
spec:
secretName: myapp-tls-secret
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
dnsNames:
- myapp.example.com
Common Anti-Patterns
NEVER: Generate Without Resource Limits
BAD:
containers:
- name: app
image: myapp:1.0
# No resources defined - pods can consume unlimited CPU/memory
GOOD:
containers:
- name: app
image: myapp:1.0
resources:
requests: { memory: "64Mi", cpu: "250m" }
limits: { memory: "128Mi", cpu: "500m" }
WHY: Unlimited resources lead to noisy neighbor issues and OOMKilled pods.
NEVER: Hardcode Secrets in ConfigMaps
BAD:
kind: ConfigMap
data:
DATABASE_PASSWORD: "my-secret-password" # Visible in etcd
GOOD:
kind: Secret
type: Opaque
stringData:
DATABASE_PASSWORD: "my-secret-password" # Base64 encoded, separate RBAC
NEVER: Use latest Tag in Production
BAD:
containers:
- name: app
image: myapp:latest # Non-deterministic, breaks rollbacks
GOOD:
containers:
- name: app
image: myapp:1.2.3 # Immutable, traceable
imagePullPolicy: IfNotPresent
NEVER: Skip Health Probes
BAD:
containers:
- name: app
image: myapp:1.0
# No probes - K8s can't detect failures
GOOD:
containers:
- name: app
image: myapp:1.0
livenessProbe:
httpGet: { path: /health, port: 8080 }
initialDelaySeconds: 30
readinessProbe:
httpGet: { path: /ready, port: 8080 }
initialDelaySeconds: 5
NEVER: Mismatch Selector Labels
BAD:
# Deployment
selector:
matchLabels: { app: myapp }
# Service
selector: { app: my-app } # Typo breaks routing
GOOD:
# Deployment
selector:
matchLabels: { app: myapp, version: v1 }
# Service
selector: { app: myapp } # Matches all versions
ALWAYS: Validate Before Delivery
Never output YAML without running it through the validation workflow:
# Run the yaml-validator workflow from this tile: syntax, schema, then dry-run
yamllint <file>.yaml
kubeconform -strict -summary <file>.yaml
kubectl apply --dry-run=server -f <file>.yaml
ALWAYS: Use Namespaces Explicitly
metadata:
name: myapp
namespace: production # Never rely on default namespace
Advanced Features
Multi-Resource Generation
- Generate each resource following the core workflow
- Use consistent labels across all resources
- Respect dependency order (e.g., ConfigMaps before Deployments)
- Validate each resource individually using the k8s-yaml-validator workflow
- Combine into a single multi-document YAML with
---separators if desired
Version-Specific Generation
- Use appropriate API versions for the target K8s version (check deprecations)
- Example: Ingress moved from
extensions/v1beta1tonetworking.k8s.io/v1in K8s 1.19+
Verification Checklist
Before delivering generated YAML, confirm:
- All required fields are present (metadata.name, metadata.namespace, spec)
- API version is correct for target K8s version
- Resource requests/limits are defined on all containers
- Health probes (liveness/readiness) are configured
- Selectors match labels exactly (no typos)
- Secrets are in Secret objects, not ConfigMaps
- Image tags are specific versions, not
latest - SecurityContext is set (runAsNonRoot, read-only filesystem where appropriate)
- Validation passes with zero errors using k8s-yaml-validator
Troubleshooting
| Issue | Solution |
|---|---|
| CRD docs not found | Try name variations; fall back to WebSearch with version-specific query |
| Validation failures | Read errors carefully; verify field names/types/required fields; re-validate |
| Wrong API version | Confirm target K8s version; check deprecation status; update apiVersion; re-validate |
Integration
- k8s-yaml-validator (in this tile) — automatic validation of generated resources
- k8s-debug (in this tile) — troubleshooting deployed resources
- helm-generator — generating Helm charts using these resources as templates
Workflow summary: Understand → Fetch CRD Docs (if needed) → Generate → Validate → Deliver
Anti-Patterns
NEVER omit resources: limits and requests from container specs
- WHY: Containers without resource constraints are evicted under node pressure, cannot be autoscaled predictably, and fail Kubernetes best-practice audits (Polaris, kube-score).
- BAD: A container spec with no
resources:block. - GOOD: Set
requestsfor scheduling andlimitsfor protection:resources: {requests: {cpu: 100m, memory: 128Mi}, limits: {cpu: 500m, memory: 512Mi}}
NEVER use latest as the image tag in Kubernetes manifests
- WHY:
imagePullPolicy: Alwayswith:latestmakes every pod start non-deterministic;imagePullPolicy: IfNotPresentwith:latestuses a stale local image silently. - BAD:
image: myapp:latest - GOOD:
image: myapp:v1.2.3with a specific, immutable tag andimagePullPolicy: IfNotPresent
NEVER expose sensitive configuration as plain environment variables
- WHY: Config mounted from a literal
envvalue appears in pod descriptions, process listings, and logs; it bypasses Kubernetes RBAC for Secret objects. - BAD:
env: [{name: DB_PASSWORD, value: "mysecret"}] - GOOD:
env: [{name: DB_PASSWORD, valueFrom: {secretKeyRef: {name: db-secret, key: password}}}]
NEVER create Deployments without liveness and readiness probes
- WHY: Without probes, Kubernetes cannot detect unhealthy pods and continues routing traffic to them even when the application is broken or deadlocked.
- BAD: A Deployment spec with no
livenessProbeorreadinessProbe. - GOOD: Add
readinessProbeto control traffic routing andlivenessProbeto trigger pod restarts on deadlocks.
NEVER inline secret values in CI pipeline commands using heredocs
- WHY: Heredoc content containing environment variable expansions appears in CI logs and shell history, leaking secrets.
- BAD:
kubectl apply -f - <<EOF\nenv:\n value: $SECRET\nEOF - GOOD: Use Kubernetes Secrets, Sealed Secrets, or external-secrets-operator; reference secrets by name rather than inlining values in pipeline commands.
References
| Topic | Reference | When to Use |
|---|---|---|
QoS classes, imagePullPolicy defaults, probe-threshold interaction, SIGTERM propagation to child processes |
Production Readiness | Before generating any Deployment or Pod spec bound for a shared or production cluster |
Explicit NEVER/WHY/consequence triplets with side-by-side bad/good examples. Generic warnings score low — concrete failure modes with real impact (security, correctness, performance) score high.
Runtime validation via eval scenarios proving the skill actually changes agent behaviour. Requires evals/ directory, instructions.json, ≥3 scenarios, and ≥80% instruction coverage in summary.json.
Appropriate rigidity for the skill type: strict rules for critical/security skills, balanced steps for process skills, flexible options for tool skills. Mismatched calibration (too rigid or too loose) reduces score.
Expert-only knowledge the AI couldn't derive on its own — domain-specific patterns, production gotchas, decision frameworks, and non-obvious tool behaviour. High scores mean <5% redundancy with what the model already knows.
A clear philosophy plus numbered step-by-step workflows with explicit entry/exit points. Should answer: what is the core principle, how to execute it, and when (and when not) to apply it.
Rich activation keywords and trigger scenarios in the description so the skill fires when needed. Best descriptions read like an exhaustive list of the tasks and concepts that should invoke this skill.
Copy-paste-ready code, complete runnable examples, real file paths and commands. Pseudocode and abstract descriptions score low — concrete, executable content scores high.
SKILL.md as a <100-line navigation hub pointing to focused references/ files. Content should be lazy-loaded on demand, not front-loaded. Includes actionable 'When to Use' conditions per reference.
Proper frontmatter, single-task focus, activation keywords, and cross-harness portability. Skills must work across 40+ agentic harnesses without hardcoded paths or agent-specific references.
Explicit NEVER/WHY/consequence triplets with side-by-side bad/good examples. Generic warnings score low — concrete failure modes with real impact (security, correctness, performance) score high.
Runtime validation via eval scenarios proving the skill actually changes agent behaviour. Requires evals/ directory, instructions.json, ≥3 scenarios, and ≥80% instruction coverage in summary.json.
Appropriate rigidity for the skill type: strict rules for critical/security skills, balanced steps for process skills, flexible options for tool skills. Mismatched calibration (too rigid or too loose) reduces score.
Expert-only knowledge the AI couldn't derive on its own — domain-specific patterns, production gotchas, decision frameworks, and non-obvious tool behaviour. High scores mean <5% redundancy with what the model already knows.
A clear philosophy plus numbered step-by-step workflows with explicit entry/exit points. Should answer: what is the core principle, how to execute it, and when (and when not) to apply it.
Rich activation keywords and trigger scenarios in the description so the skill fires when needed. Best descriptions read like an exhaustive list of the tasks and concepts that should invoke this skill.
Copy-paste-ready code, complete runnable examples, real file paths and commands. Pseudocode and abstract descriptions score low — concrete, executable content scores high.
SKILL.md as a <100-line navigation hub pointing to focused references/ files. Content should be lazy-loaded on demand, not front-loaded. Includes actionable 'When to Use' conditions per reference.
Proper frontmatter, single-task focus, activation keywords, and cross-harness portability. Skills must work across 40+ agentic harnesses without hardcoded paths or agent-specific references.
Expert-only knowledge the AI couldn't derive on its own — domain-specific patterns, production gotchas, decision frameworks, and non-obvious tool behaviour. High scores mean <5% redundancy with what the model already knows.
A clear philosophy plus numbered step-by-step workflows with explicit entry/exit points. Should answer: what is the core principle, how to execute it, and when (and when not) to apply it.
Explicit NEVER/WHY/consequence triplets with side-by-side bad/good examples. Generic warnings score low — concrete failure modes with real impact (security, correctness, performance) score high.
Proper frontmatter, single-task focus, activation keywords, and cross-harness portability. Skills must work across 40+ agentic harnesses without hardcoded paths or agent-specific references.
SKILL.md as a <100-line navigation hub pointing to focused references/ files. Content should be lazy-loaded on demand, not front-loaded. Includes actionable 'When to Use' conditions per reference.
Appropriate rigidity for the skill type: strict rules for critical/security skills, balanced steps for process skills, flexible options for tool skills. Mismatched calibration (too rigid or too loose) reduces score.
Rich activation keywords and trigger scenarios in the description so the skill fires when needed. Best descriptions read like an exhaustive list of the tasks and concepts that should invoke this skill.
Copy-paste-ready code, complete runnable examples, real file paths and commands. Pseudocode and abstract descriptions score low — concrete, executable content scores high.
Runtime validation via eval scenarios proving the skill actually changes agent behaviour. Requires evals/ directory, instructions.json, ≥3 scenarios, and ≥80% instruction coverage in summary.json.
No eval scenarios available.
Production Readiness: Non-Obvious Generation Gotchas
Deep-dive reference for k8s-yaml-generator. Load this when generating a
Deployment or Pod spec that will run outside a scratch/dev cluster — the
defaults Kubernetes silently applies are frequently not the defaults an
author expects, and getting them wrong only surfaces under load or during a
node drain, not at kubectl apply time.
imagePullPolicy defaults are tag-dependent, not a single default
Kubernetes does not apply one blanket default for imagePullPolicy. The
default is derived from the image tag:
image: myapp:latest(or no tag at all) → default isAlways. Every pod start re-pulls the image, even if the same tag was pulled seconds ago on the same node.image: myapp:v1.2.3(any non-latesttag) → default isIfNotPresent. A stale local image on a node is reused silently if the tag was already cached there, even if the registry’sv1.2.3has since been overwritten.
Both defaults are traps in different directions: latest + Always makes
rollouts non-deterministic and slow (registry round-trip on every pod start);
a pinned tag + IfNotPresent can serve a stale image forever if someone
force-pushes the same tag to the registry. Always set imagePullPolicy
explicitly rather than relying on either inferred default, and never reuse a
tag once it has been pushed.
Requests without limits change the pod’s QoS class
The combination of resources.requests and resources.limits determines
the pod’s Quality of Service class, which controls eviction order under node
memory pressure:
| requests | limits | QoS class | Eviction priority |
|---|---|---|---|
| unset | unset | BestEffort |
Evicted first |
| set, requests < limits | set | Burstable |
Evicted second |
| set | set, requests == limits | Guaranteed |
Evicted last |
A manifest with limits but no requests is silently treated as
requests == limits (Kubernetes back-fills requests from limits when
requests is omitted), so it is never actually BestEffort — but a manifest
with requests and no limits stays Burstable with an unbounded ceiling,
which defeats the purpose of setting requests in the first place. For a
production workload that must survive a memory-pressure eviction event
without being picked first, set both fields, with requests == limits, to
land in Guaranteed.
Probe threshold interaction can create restart storms
livenessProbe and readinessProbe are evaluated independently, but tuning
them identically is a common cause of cascading restarts under load: if
initialDelaySeconds and periodSeconds are equal for both probes and the
application is briefly slow to respond (a GC pause, a cold cache), the
liveness probe can fail at the same moment the readiness probe is already
pulling the pod from service — the kubelet then restarts a container that
was about to recover on its own, resetting warm caches and connections and
making the underlying slowness worse. Give the liveness probe a longer
failureThreshold × periodSeconds window than the readiness probe, so
readinessProbe can take the pod out of rotation without livenessProbe
racing to kill it.
terminationGracePeriodSeconds does not wait for child processes
The default 30-second grace period sends SIGTERM to PID 1 in the
container and waits; it does not propagate to child processes unless PID 1
forwards the signal itself. A shell-form CMD/ENTRYPOINT (CMD myapp --flag) runs the shell as PID 1 and the application as a child that never
receives SIGTERM directly — the shell exits, orphaning the child, which is
then hard-killed by SIGKILL at the end of the grace period with no chance
to flush in-flight work. Use exec-form entrypoints (["myapp", "--flag"]),
or a lightweight init (tini, dumb-init) as PID 1, so SIGTERM reaches
the actual process.