Skip to main content

loom-kustomize

Kubernetes-native configuration management with Kustomize.

Source facts

Repository
cosmix/loom
Last source activity
August 29, 2026 at 21:19
Detected SKILL.md language
English
Stars
61
Forks
4

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
loom-kustomize
description
Kubernetes-native configuration management with Kustomize.
allowed-tools
["Read","Grep","Glob","Edit","Write","Bash"]
triggers
["kustomize","kustomization","overlay","base","patch","strategic merge","json patch","json6902","configmap generator","secret generator","namespace","namePrefix","nameSuffix","commonLabels","commonAnnotations","component","transformer","replacement","multi-environment","dev/staging/prod configs","k8s manifest management"]
# Kustomize Skill ## Overview Template-free, declarative Kubernetes config management via merging. `kubectl apply -k <dir>` and `kustomize build <dir>` render an overlay. Failure modes here are overwhelmingly **silent** (wrong output, exit 0) — always `diff` the `kustomize build` output before/after any structural change. ## Core Concepts | Term | Meaning | | --------------- | ------------------------------------------------------------------------------------------------ | | **Base** | Dir with `kustomization.yaml` + shared resources; never modified in place. | | **Overlay** | Dir referencing a base under `resources:`, applying env-specific patches/generators. | | **Patch** | Partial resource that modifies existing ones — strategic merge (default) or JSON 6902. | | **Component** | Reusable opt-in feature bundle (`kind: Component`); runs against the parent's accumulated set. | | **Generator** | Builds ConfigMaps/Secrets from literals/files/envs, appending a content-hash name suffix. | | **Transformer** | Cross-cutting mutation: `namespace`, `namePrefix`/`nameSuffix`, `labels`, `images`, `replicas`. | | **Replacement** | Copies a field value from a source resource into target field paths (successor to `vars`). | ## Directory Layout ```text k8s/ ├── base/ # shared, env-agnostic (kustomization.yaml + resources) ├── overlays/ │ ├── dev/ # low resources, debug │ ├── staging/ # + monitoring component │ └── prod/ # HA, security, digest-pinned images └── components/ # opt-in features: monitoring, ingress, debug-tools ``` Keep the hierarchy **shallow** (base → overlay, not base → overlay1 → overlay2). Paths in `resources:` are relative to the kustomization.yaml's own directory. ## Quick Reference | Task | Command | | ---------------- | -------------------------------------------------------------------------- | | Build | `kustomize build k8s/overlays/prod` | | Preview diff | `kubectl diff -k k8s/overlays/prod` | | Apply | `kubectl apply -k k8s/overlays/prod` | | Validate | `kustomize build k8s/overlays/prod \| kubectl apply --dry-run=client -f -` | | Migrate deprecated fields | `kustomize edit fix` (idempotent; run per dir; see Idioms) | | Set image tag | `kustomize edit set image myapp=registry/myapp:v1.2.3` | | Add ConfigMap | `kustomize edit add configmap app-config --from-literal=KEY=value` | **Generators** (`configMapGenerator` / `secretGenerator` items): `literals:` (`K=v`), `files:` (`app.properties`, or `alias=path`), `envs:` (`config.env`). Secrets add `type:` (e.g. `kubernetes.io/tls`). `behavior:` = `create` (default) | `merge` | `replace`. **Transformers** (top-level keys): `namespace`, `namePrefix`, `nameSuffix`, `labels` (use over deprecated `commonLabels`), `commonAnnotations`, `images` (`newName`/`newTag`/`digest`), `replicas` (`- name: x count: n`), `replacements`. ## Patch Strategies | Aspect | Strategic Merge (default) | JSON 6902 (RFC 6902) | | ------------- | ----------------------------------------------------------- | ------------------------------------------------------ | | Merge model | K8s-schema-aware; maps by key, lists merge on known key | `op` (`add`/`remove`/`replace`/`move`/`copy`/`test`) on JSON Pointer paths | | Best for | Field/env/resource-limit tweaks, adding containers | Precise array-index ops, conditional `test`, CRDs | | Whole-list replace | **Unreliable** — `$patch: replace` regressed (issue #2980, ignored/merged instead) | Reliable: `op: replace` on the list path | Both go under the unified `patches:` field, which auto-detects type from content and whose `target` supports `group/version/kind/name/namespace` + `labelSelector`/`annotationSelector` (one patch → many resources). `patchesStrategicMerge`/`patchesJson6902` are deprecated (v5.0.0) and gone from the v1 API. ## Components Reusable opt-in bundles for features only some environments need (monitoring, ingress, debug tools, security hardening). Use a **patch** instead for required, always-applied, or env-specific value tweaks. ```yaml # k8s/components/monitoring/kustomization.yaml apiVersion: kustomize.config.k8s.io/v1alpha1 # alpha — API may change without deprecation window kind: Component resources: - servicemonitor.yaml patches: - path: patch-metrics.yaml target: {kind: Deployment} labels: - pairs: {prometheus.io/scrape: "true"} includeSelectors: false ``` > **Reference Components under `components:`, never `resources:`.** A Component placed under `resources:` is silently misapplied as a plain manifest; kustomize enforces the split (Components rejected from `resources:`, Kustomizations from `components:`). Components run against the parent's accumulated resource set and **can be nested**. ## Examples ### Base kustomization + Deployment ```yaml # k8s/base/kustomization.yaml apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization namespace: myapp # Use the labels transformer, NOT deprecated commonLabels (which mutates the # IMMUTABLE spec.selector.matchLabels → delete/recreate on any later change). # includeSelectors defaults false (safe on live resources); includeTemplates # reaches pod templates. Define real selector labels directly in deployment.yaml. labels: - pairs: {app: myapp, managed-by: kustomize} includeSelectors: false includeTemplates: true commonAnnotations: {contact: team@example.com} resources: - deployment.yaml - service.yaml - serviceaccount.yaml configMapGenerator: - name: app-config literals: [LOG_LEVEL=info, MAX_CONNECTIONS=100] images: - name: myapp newName: registry.example.com/myapp newTag: latest ``` ```yaml # k8s/base/deployment.yaml (selector label defined explicitly — not injected) apiVersion: apps/v1 kind: Deployment metadata: {name: myapp} spec: replicas: 1 selector: matchLabels: {app: myapp} template: metadata: labels: {app: myapp} spec: serviceAccountName: myapp containers: - name: app image: myapp ports: [{containerPort: 8080, name: http}] envFrom: [{configMapRef: {name: app-config}}] resources: requests: {memory: 128Mi, cpu: 100m} limits: {memory: 256Mi, cpu: 200m} readinessProbe: httpGet: {path: /ready, port: http} initialDelaySeconds: 5 ``` ### Production overlay (annotated) ```yaml # k8s/overlays/prod/kustomization.yaml apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization namespace: myapp-prod labels: - pairs: {environment: prod, criticality: high} includeSelectors: false includeTemplates: true commonAnnotations: oncall: sre-team@example.com # SINGLE resources: block. A second resources: key later in the same document # silently wins (duplicate YAML map keys), dropping ../../base entirely. resources: - ../../base - poddisruptionbudget.yaml - horizontalpodautoscaler.yaml - networkpolicy.yaml patches: - path: patch-resources.yaml # bump requests/limits - path: patch-affinity.yaml # required podAntiAffinity + nodeAffinity - path: patch-security.yaml # runAsNonRoot, drop ALL, RO rootfs, seccomp configMapGenerator: - name: app-config behavior: merge envs: [config-values.env] secretGenerator: - name: app-secrets envs: [secrets.env] # gitignored / external-managed images: - name: myapp newTag: v1.2.3 digest: sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 # pin by digest in prod replicas: - {name: myapp, count: 5} components: - ../../components/monitoring - ../../components/ingress ``` Overlay patch files are minimal partial docs matched by `kind`/`name`, e.g. a security patch: ```yaml # patch-security.yaml apiVersion: apps/v1 kind: Deployment metadata: {name: myapp} spec: template: spec: securityContext: runAsNonRoot: true runAsUser: 1000 seccompProfile: {type: RuntimeDefault} containers: - name: app securityContext: allowPrivilegeEscalation: false readOnlyRootFilesystem: true capabilities: {drop: [ALL]} ``` ### JSON 6902 patch (precise array ops) ```yaml # kustomization.yaml patches: - path: json-patch.yaml target: {group: apps, version: v1, kind: Deployment, name: myapp} ``` ```yaml # json-patch.yaml — list of {op, path, value}; auto-detected as JSON 6902 - op: add # append sidecar path: /spec/template/spec/containers/- value: {name: log-shipper, image: fluent/fluent-bit:2.0} - op: replace # container 0 image path: /spec/template/spec/containers/0/image value: registry.example.com/myapp:v1.2.3 - op: remove # env var by index path: /spec/template/spec/containers/0/env/3 - op: test # conditional guard path: /spec/replicas value: 1 - op: replace path: /spec/replicas value: 5 ``` ### Generators ```yaml configMapGenerator: - name: app-config behavior: merge # create (default) | replace | merge literals: [LOG_LEVEL=debug] # overrides base key files: [app.properties, tls.crt=certs/server.crt] envs: [config.env] options: disableNameSuffixHash: true # ⚠ defeats config-driven rollouts (see Gotchas) secretGenerator: - name: tls-secrets files: [tls.crt, tls.key] type: kubernetes.io/tls - name: app-secrets envs: [secrets.env] # gitignored — never commit secrets ``` ### Replacements (propagate a field value) ```yaml replacements: # Propagate hashed ConfigMap name into the Deployment volume ref - source: {kind: ConfigMap, name: app-config, fieldPath: metadata.name} targets: - select: {kind: Deployment} fieldPaths: - spec.template.spec.volumes.[name=config].configMap.name ``` ### Remote bases ```yaml resources: - https://github.com/org/repo//k8s/base?ref=v1.0.0 # pin to tag or full SHA (see Security) ``` ## Common Tasks ```bash # Validate / preview kustomize build k8s/overlays/prod | kubectl apply --dry-run=server -f - kubectl diff -k k8s/overlays/prod # Inspect name/hash transformations kustomize build k8s/overlays/prod | rg '^ name:' kustomize build k8s/overlays/prod | rg -A2 'kind: ConfigMap' # Migrate deprecated fields in place (idempotent; wrap in a build-diff) kustomize edit fix # Bootstrap kustomization.yaml from existing manifests kustomize create --autodetect # or --resources deployment.yaml,service.yaml ``` ## GitOps & CI/CD Integration **ArgoCD**: `Application.spec.source.path: k8s/overlays/prod`, `syncPolicy.automated {prune, selfHeal}`. **Flux**: `kustomize.toolkit.fluxcd.io/v1 Kustomization` with `spec.path`, `prune: true`, `sourceRef` (GitRepository), `healthChecks`. Both bundle their **own** kustomize version — pin/match it (see Currency gotcha). Prefer the controller's authenticated Git source over remote bases in `resources:`. ```bash # CI: render, then validate. kubeval is unmaintained — use kubeconform. kustomize build "k8s/overlays/${OVERLAY}" > all.yaml kubeconform -strict -summary -kubernetes-version 1.29.0 all.yaml conftest test all.yaml # OPA policy gate ``` ### Helm inflation gotcha
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub