Kustomize
Customize Helm-rendered Kubernetes manifests with Kustomize overlays.
Kustomize can be used to customize Kubernetes manifests with bases, overlays, and patches.
For Pangolin and Newt, the supported Kustomize workflow is to render the Helm charts into manifests and use those rendered manifests as the Kustomize base.
Use Kustomize when you need:
- environment-specific overlays for dev, staging, or production
- explicit manifest patches in Git
- a manifest-driven workflow for GitOps tools
- small changes on top of a shared base without maintaining separate full manifests
Supported workflow
The chart repository does not provide native Kustomize bases. Use this workflow instead:
Render chart manifests
Render the Helm chart with your values file and save the output as base manifests.
Commit base manifests
Commit rendered manifests as the Kustomize base in Git.
Create environment overlays
Create overlays for each environment (for example dev, staging, production).
Apply or reconcile
Apply overlays manually or reconcile them with Argo CD or Flux.
Do not manage the same resources with both a live Helm release and Kustomize. Pick one ownership model per environment.
Recommended ownership model:
- Use Helm only to render manifests.
- Use Kustomize, Argo CD, or Flux to apply and reconcile the rendered manifests.
- Re-render the base when upgrading the chart version.
Example repository layout
my-pangolin-k8s/
├── base/
│ ├── kustomization.yaml
│ ├── pangolin.yaml
│ └── newt.yaml
├── overlays/
│ ├── dev/
│ │ ├── kustomization.yaml
│ │ └── pangolin-resources.patch.yaml
│ ├── staging/
│ │ ├── kustomization.yaml
│ │ └── pangolin-resources.patch.yaml
│ └── prod/
│ ├── kustomization.yaml
│ └── pangolin-resources.patch.yaml
└── values/
├── values-pangolin.yaml
└── values-newt.yamlStep 1: Render manifests from Helm
Create a base directory:
mkdir -p base overlays/dev overlays/staging overlays/prodRender Pangolin:
helm template pangolin fossorial/pangolin \
--namespace pangolin \
--values values/values-pangolin.yaml \
> base/pangolin.yamlRender Newt:
helm template newt fossorial/newt \
--namespace pangolin \
--values values/values-newt.yaml \
> base/newt.yamlStep 2: Create the base kustomization
# base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- pangolin.yaml
- newt.yamlStep 3: Create an overlay
Use resources to reference the base.
# overlays/prod/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
labels:
- pairs:
app.kubernetes.io/environment: production
app.kubernetes.io/managed-by: kustomize
patches:
- path: pangolin-resources.patch.yaml
target:
group: apps
version: v1
kind: Deployment
name: pangolinAvoid namePrefix and nameSuffix for Helm-rendered bases unless you have verified every generated reference. Renaming chart-generated resources can break service names, selectors, secret references, and workload dependencies.
Step 4: Add patches
Example Strategic Merge patch for container resources:
# overlays/prod/pangolin-resources.patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: pangolin
spec:
template:
spec:
containers:
- name: pangolin
resources:
requests:
cpu: 1000m
memory: 1Gi
limits:
memory: 2GiExample JSON6902-style inline patch:
# overlays/prod/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
patches:
- target:
group: apps
version: v1
kind: Deployment
name: pangolin
patch: |-
- op: replace
path: /spec/template/spec/containers/0/resources/requests/cpu
value: "1000m"Modern Kustomize uses the patches field for both Strategic Merge and JSON6902-style patches. Avoid patchesStrategicMerge, patchesJson6902, and bases in new examples.
Apply an overlay
Preview the rendered output:
kustomize build overlays/prodCompare with the live cluster:
kustomize build overlays/prod | kubectl diff -f -Apply the overlay:
kubectl apply -k overlays/prodOr apply the rendered output:
kustomize build overlays/prod | kubectl apply -f -Updating the base
When upgrading chart versions or changing Helm values, re-render the base and review the diff.
helm repo update fossorialRender the updated chart output:
helm template pangolin fossorial/pangolin \
--namespace pangolin \
--values values/values-pangolin.yaml \
> base/pangolin.yamlhelm template newt fossorial/newt \
--namespace pangolin \
--values values/values-newt.yaml \
> base/newt.yamlThen validate the overlay:
kustomize build overlays/prodReview changes before applying:
git diff
kustomize build overlays/prod | kubectl diff -f -Apply after review:
kubectl apply -k overlays/prodImportant considerations
Namespace handling
Render the charts with the namespace you intend to use:
helm template pangolin fossorial/pangolin \
--namespace pangolin \
--values values/values-pangolin.yaml \
> base/pangolin.yamlCreate the namespace before applying the overlay:
kubectl create namespace pangolinApply any required Pod Security Admission labels or cluster-policy labels before workloads are created.
Secrets
Do not commit plaintext secrets into rendered manifests.
Use one of these approaches instead:
- reference existing Kubernetes Secrets in the values file before rendering
- create secrets separately with your secret-management workflow
- use Sealed Secrets, External Secrets Operator, SOPS, or another GitOps-safe secret solution
Do not mix ownership models
Avoid this pattern:
helm upgrade pangolin fossorial/pangolin
kubectl apply -k overlays/prodThis creates two tools managing the same objects.
Use one of these models instead:
| Model | Description |
|---|---|
| Helm-managed | Helm installs and upgrades the live release. Kustomize is not used for the same objects. |
| Kustomize-managed | Helm only renders the base. Kustomize applies and owns the live objects. |
| GitOps-managed | Argo CD or Flux applies the Kustomize overlay and owns reconciliation. |
Troubleshooting
Validate the overlay:
kustomize build overlays/prodCheck the generated YAML:
kustomize build overlays/prod > manifests.yamlRun a server-side dry run:
kubectl apply -f manifests.yaml --dry-run=serverPreview live changes:
kubectl diff -f manifests.yamlCheck live resources:
kubectl get all -n pangolin
kubectl get events -n pangolin --sort-by=.lastTimestamp
