Kubernetes packaging in 2026: revisit Helm, Kustomize and Timoni before you template another YAML file
Sooner or later every team on Kubernetes has the same argument. One person says Helm charts have become unreadable and the team should move to plain YAML with Kustomize overlays. Another says YAML is the problem, and the team should write its manifests in a typed language such as CUE with Timoni, or in TypeScript with cdk8s. A third person quietly points out that the current charts work, and that the last production incident had nothing to do with templates.
This post compares the three tools that most teams actually weigh for packaging and managing Kubernetes manifests in 2026: Helm, Kustomize and Timoni. The focus is not which syntax looks nicer. It is the daily work around them: how each one renders and applies manifests, how packages are distributed through OCI registries, how signing and provenance work, where secrets end up, what happens on drift, upgrade and rollback, and what each costs to run for a small team. We look at where each tool stands on 27 October 2026, and work through a plan for one invented team that was about to rewrite all its charts.
Two earlier posts sit next to this one. In Argo CD and Flux we compared the GitOps controllers that pull manifests from Git and keep a cluster in sync. Both of them use Helm and Kustomize underneath, so the packaging choice in this post feeds straight into that one. In container image signing with Cosign, Notation and in-toto attestations we looked at signing images. Charts and modules stored in OCI registries can be signed with the same tools, and most teams forget to do it.
A scenario to keep us honest
Take an invented Thrissur company that builds appointment, pharmacy and billing software for small hospitals and clinics in central Kerala. About 18 engineers, of whom two look after the platform part time. The product runs as 14 services on a managed Kubernetes cluster for the SaaS customers. Two larger hospital groups run the same software on their own clusters in their own data centres, and the team ships them releases every month.
Their packaging setup looks like this:
- Every service has its own Helm chart. All 14 started in 2022 as copies of the output of
helm createand have drifted apart since. Fixing a probe or a security context means 14 merge requests. - CI runs Helm 3 and deploys with
helm upgrade --install --atomicfrom GitLab pipelines, withvalues-dev.yaml,values-staging.yamlandvalues-prod.yamlper chart. - Third-party charts (cert-manager, a PostgreSQL operator, a monitoring stack) come from classic HTTP chart repositories, pinned by version only.
- Database passwords are GitLab CI variables passed with
--set. - For the two on-premises customers, the team emails a zip of chart folders and a values file. The customers’ admins run
helm installthemselves. - No charts are signed. Nobody checks which chart digest was actually installed.
Two incidents set off the current discussion. First, a typo in a production values file (replicaCont instead of replicaCount) was accepted silently, and the appointment service ran on one pod during a Monday morning rush. Second, a support engineer fixed a memory limit directly with kubectl edit during an outage, and the next deployment quietly undid the fix.
After the second incident, one developer proposed moving everything to Kustomize (“no more templates”). Another, who had been reading about CUE, proposed Timoni (“types would have caught the typo”).
The question this post tries to answer: is a packaging tool switch the right fix for this team, and if not, what is?
The short version
- Helm (Apache-2.0, CNCF Graduated) is the package manager most of the Kubernetes world publishes in. Helm 4, released in November 2025, brought server-side apply, kstatus-based waiting, WebAssembly plugins, install by OCI digest and reproducible chart archives. Helm 4.3.1 is the latest release. Helm 3 gets security fixes only until 10 February 2027, and nothing after that.
- Kustomize (Apache-2.0, a Kubernetes SIG CLI project) is template-free: plain YAML bases plus overlays and patches. It is built into
kubectland into both Argo CD and Flux. It has no package format, no release history and no rollback of its own. The latest release is 5.8.3. - Timoni (Apache-2.0, not a CNCF project) packages Kubernetes apps as CUE modules distributed only as OCI artifacts, with type checking, Cosign or Notation signing, server-side apply, drift detection and staged applies. It is well designed and actively developed, but it is pre-1.0 (0.36.0), its README still warns that APIs may change in backwards incompatible ways, and most of its commits come from one maintainer.
- For the Thrissur team, the plan in this post is: move to Helm 4 before Helm 3 support ends, collapse 14 copied charts into one shared chart with a strict values schema, publish charts to an OCI registry and install them by digest with signatures, take secrets out of
--set, and handle drift with a GitOps controller. Kustomize stays for small last-mile patches. Timoni gets a bounded pilot on one internal service. - Neither incident was caused by Helm’s template language. Both were caused by missing checks. A new tool does not add checks unless you add them.
Where things stand on 27 October 2026
All times below are converted to IST.
Helm
- Latest releases: Helm 4.3.1 on 15 October 2026 at about 6:38 AM IST, and Helm 3.22.1 later the same day at about 10:32 PM IST. Helm 4.3.0 and 3.22.0 came out on 10 September 2026, early morning IST.
- Helm 4.0.0 was released on 12 November 2025 during KubeCon North America. It was the first new major version in six years. The release notes list a redesigned plugin system with optional WebAssembly plugins, post-renderers as plugins, server-side apply, waiting based on kstatus, content-based caching, structured logging, reproducible chart archives, and an SDK that can support more than one chart API version.
- Server-side apply in Helm 4: new releases use it by default. For upgrades and rollbacks, Helm keeps whatever method the release used before, so releases created by Helm 3 continue with client-side apply after you move to Helm 4 unless you pass
--server-sideexplicitly. - Renamed flags:
--atomicis now--rollback-on-failureand--forceis now--force-replace. The old names still work with a deprecation warning. Post-renderers must now be plugins, not arbitrary executables, andhelm registry logintakes only a domain name. - Charts: chart
apiVersion: v2, which nearly every chart uses today, continues to work. A new chart API v3 is experimental behind theHELM_EXPERIMENTAL_CHART_V3environment variable. - Helm 4.3.0 added an ownership check before deleting resources during uninstall, support for
SOURCE_DATE_EPOCHwhen packaging charts, a--descriptionflag for rollbacks, and support for GnuPG keybox keyrings in provenance verification. - Helm 3 end of life: the Helm blog post of 2 June 2026 set bug fixes up to the final, limited Helm 3 feature release on 9 September 2026 (that was 3.22.0, limited to Kubernetes client library updates), and security fixes until 10 February 2027, three months later than first planned. After that date there will be no Helm 3 releases of any kind.
- Security in 2026: the Helm repository lists three advisories published on 9 and 10 April 2026 (IST): a critical one where plugin verification failed open when the
.provfile was missing (CVE-2026-35205), a high-rated path traversal through plugin metadata that allowed writing files outside the plugin directory (CVE-2026-35204), and a medium-rated issue in chart extraction (CVE-2026-35206). Fixes landed in Helm 4.1.4 and, for the last one, 3.20.2. Two of the three are about plugins, which is worth remembering when someone suggests installing one more plugin in CI. - Licence and governance: Apache-2.0. Helm joined the CNCF as an incubating project on 1 June 2018 and graduated on 1 May 2020.
Kustomize
- Latest release: kustomize 5.8.3 on 8 October 2026 at about 5:19 PM IST. Its notes say it only removes dependencies that were a problem when updating Kustomize inside
kubectl, with no functional changes. Version 5.8.2 (30 September 2026) addedkustomize edit add configurationandkustomize edit remove component, and loads its built-in OpenAPI schema from a bundle embedded in the binary. The notes add that the schema is still based on Kubernetes v1.21.2. - Version 5.8.1 (9 February 2026) fixed a namespace propagation problem from 5.8.0 and added support for Helm 4 alongside Helm 3 in the Helm chart inflation feature.
- Inside
kubectl: Kustomize has been part ofkubectlsince Kubernetes 1.14 in March 2019. In our drill,kubectlv1.37.1 reported an embedded Kustomize v5.8.1, two patch versions behind the standalone binary. The version you get withkubectl apply -kis whatever yourkubectlcarries. - Licence and governance: Apache-2.0, in the
kubernetes-sigsorganisation and sponsored by Kubernetes SIG CLI, so it sits under the Kubernetes project in the CNCF. The repository lists no security advisories.
Timoni
- Latest release: Timoni 0.36.0 on 21 October 2026 at about 10:55 PM IST, which adds a
--resolve-symlinksflag to build commands. Version 0.35.0 (28 September 2026) added validation of custom resources against their CRD schemas and CEL rules at build time, an offline mode fortimoni bundle vet, a signed packslip in the release assets for verifying downloads, and Kubernetes 1.37 libraries. - The binary in our drill reported CUE engine 0.17.1 and the API version
timoni.sh/v1alpha1. - Stability: the project README says Timoni is under active development, still in its infancy, and that the APIs and command line may change in a backwards incompatible manner.
- People: Timoni is created by Stefan Prodan, a long-time Flux maintainer, and the documentation includes a guide for running Timoni with Flux. The GitHub contributors list shows most commits from him. The project asks users to sponsor its development.
- Licence and governance: Apache-2.0. Not a CNCF project. No security advisories are listed in the repository.
Nearby options, briefly
- Helmfile (MIT, latest 1.8.2 on 16 October 2026) declares many Helm releases and their values in one file and applies them together. Useful when you run many third-party charts per cluster without a GitOps controller.
- cdk8s (Apache-2.0, CNCF Sandbox since November 2020) generates Kubernetes YAML from TypeScript, Python, Java or Go code. The CLI ships very frequent releases (2.207.70 on 21 October 2026).
- KCL (Apache-2.0, CNCF Sandbox since September 2023) is a typed configuration language with Kubernetes tooling. Latest 0.13.2 on 19 October 2026.
- Jsonnet with Tanka (both Apache-2.0) is Grafana’s approach: Jsonnet libraries rendered by Tanka. Tanka 0.40.0 came out on 26 October 2026.
- All of these produce manifests. None of them removes the need for review, schema checks, signing and a clear apply path.
First question: why do you want to change?
Write down the real reason before comparing tools. Each reason has a different, and often cheaper, answer.
- “Our charts are copied and drifting.” That is a structure problem. One shared chart, or a library chart, fixes it within Helm.
- “A typo in values went to production.” That is a validation problem. A
values.schema.jsonwithadditionalProperties: falsecatches unknown keys in Helm. Timoni and other typed tools catch them by design. - “Someone’s hotfix was overwritten.” That is a drift and process problem. A GitOps controller shows and corrects drift. Changing the packaging tool alone does not stop the next
kubectl edit. - “Templates are hard to read.” Sometimes true. Often it means each chart has grown options nobody uses. Deleting options is cheaper than changing tools.
- “We need to ship the software to customers.” Then you need a package format, versioning and signing. Helm and Timoni give you that. Kustomize does not.
- “Helm 3 is going out of support.” True, and urgent. The answer is Helm 4, which keeps v2 charts working.
For the Thrissur team, the honest list was: copied charts, no schema, no drift visibility, secrets on the command line, an unsigned zip for on-premises customers, and a Helm version that leaves support in February 2027. None of these needs a new packaging tool.
What each tool is
Helm
Helm is a package manager. A chart is a folder with Chart.yaml, default values.yaml, Go templates under templates/, optional CRDs under crds/, and optional dependencies on other charts. Helm merges your values over the defaults, renders the templates into Kubernetes YAML, and applies the result as a release.
A release is Helm’s own record of what it installed. Each revision is stored in the cluster as a Secret in the release namespace, containing the chart metadata, the values and the full rendered manifest. That record is what makes helm history, helm rollback and helm uninstall possible. It is also why anyone who can read Secrets in that namespace can read every value you passed, including passwords given with --set.
Helm also has hooks (jobs run before or after install and upgrade), tests (pods run by helm test), library charts for shared templates, and a large public ecosystem. Most vendors and open source projects publish a Helm chart first.
Kustomize
Kustomize takes the opposite position: no templates at all. You keep plain Kubernetes YAML as a base, and write overlays that change it for each environment: set a namespace, change an image tag, change replicas, apply strategic merge or JSON patches, add labels, and generate ConfigMaps and Secrets with a content hash in the name so that pods roll when config changes. Components let you share optional pieces across overlays.
Kustomize is a renderer only. kustomize build prints YAML. Applying, pruning and history belong to whatever applies the output: kubectl apply -k, Argo CD or Flux. It can also inflate a Helm chart through its helmCharts field when you pass --enable-helm, and then patch the result. Many teams use it exactly this way for third-party charts.
Timoni
Timoni is a package manager built on CUE. A module is the equivalent of a chart, an instance of a release, and a bundle of an umbrella chart. Module authors write Kubernetes objects in CUE, with types generated from the Kubernetes API and from CRDs, so a wrong type or an unknown field fails at build time. Users give values in CUE, YAML or JSON.
Modules are distributed only as OCI artifacts in container registries, can be pinned by digest, and can be signed and verified with Cosign or Notation. On apply, Timoni uses server-side apply, applies resources in stages (for example CRDs and namespaces first, then workloads), waits for health, prunes objects that are no longer part of the instance, and records an inventory of object IDs in a Secret. timoni apply --dry-run --diff shows what would change on the cluster and exits with code 1 when it finds drift. Bundles and the Runtime API let one file describe many instances across several clusters, with values read from cluster Secrets, CI environment variables, 1Password or SOPS files.
Side by side
How manifests are produced
- Helm: Go templates plus the Sprig function library over YAML text. Very flexible, which is both its strength and the reason big charts become hard to read. Rendering is text-based, so indentation mistakes and type mistakes show up only when the output is parsed.
- Kustomize: structured patches over valid YAML. What you read in the base is what gets deployed, minus the overlay changes. There are no loops or conditions, so repeating patterns needs components or copies.
- Timoni: CUE, a typed configuration language. Values and templates are checked against schemas, and conflicting values are errors, not silent overrides. The cost is learning CUE, which is unfamiliar to most teams.
Validation of values
- Helm: none by default. A chart can ship
values.schema.json(JSON Schema), which Helm checks on install, upgrade, lint and template. Unknown keys are accepted unless the schema setsadditionalProperties: false. In our drill, the chart fromhelm createaccepted both a misspelt key andreplicaCount=threewithout error. - Kustomize: checks that its input is valid YAML and that patch targets exist. It does not validate fields against the Kubernetes schema; use a tool such as kubeconform or a server-side dry run for that.
- Timoni: strict by design. In our drill, a misspelt values key failed with “field not allowed” and a string for replicas failed with a type error. Since 0.35.0 it also checks custom resources against CRD schemas and CEL rules at build time.
Applying, upgrades and drift
- Helm:
helm upgraderenders the new manifest and applies it. Helm 3 used a three-way merge on the client side. Helm 4 uses server-side apply for new releases and keeps the old method for existing ones. Waiting uses kstatus in Helm 4. Helm does not watch the cluster after the command ends, so drift is invisible unless you run something like the helm-diff plugin or a GitOps controller. - Kustomize: no apply logic of its own. With plain
kubectl apply -k, deleted resources are not removed unless you usekubectl’s pruning options carefully. With Argo CD or Flux, you get drift detection and pruning from the controller. - Timoni: server-side apply, staged apply with health checks, pruning from its inventory, and a diff command that detects drift. It can correct drift by applying again and waiting for the rollout. Like Helm, it does not run continuously unless you put it behind a controller or a scheduled job.
Rollback
- Helm:
helm rollback <release> <revision>re-applies a stored revision. It does not undo database migrations, data changes, or CRD changes, because Helm does not upgrade CRDs from thecrds/folder after the first install. The default history kept on upgrade is 10 revisions. - Kustomize: none. You roll back by reverting the Git commit and letting the controller or pipeline apply the previous output.
- Timoni: no separate rollback command. You apply the previous module version, ideally by digest, and Timoni prunes and waits as usual. The inventory records the last applied objects, not a full history.
CRDs
- Helm: CRDs in
crds/are installed on first install only, are not templated, and are not upgraded or deleted. Many charts work around this by putting CRDs intemplates/, which brings its own risks during uninstall. - Kustomize: CRDs are just resources. Strategic merge patches on custom resources need extra OpenAPI configuration, because the built-in schema only knows core Kubernetes types (and an old version of them).
- Timoni: CRDs can be templated and upgraded, and custom resources are validated against them. Its garbage collector can delete CRDs and PersistentVolumeClaims on uninstall, so module authors need to mark what must be kept.
OCI distribution
- Helm: OCI registries have been fully supported since Helm 3.8.
helm pushuploads a packaged chart, and installs can use a digest, for exampleoci://registry.example.in/charts/app@sha256:.... Helm 4 refuses a chart whose digest does not match. Classicindex.yamlHTTP repositories still work, and many third-party projects still publish there. - Kustomize: no package format. Bases can be local folders or remote Git URLs. Flux can deliver Kustomize folders as OCI artifacts, but that is Flux’s feature, not Kustomize’s.
- Timoni: OCI only, by design. Modules are versioned with semantic versions and can be referenced by digest.
Signing and provenance
- Helm: the built-in provenance system uses OpenPGP.
helm package --signwrites a.provfile, andhelm install --verifychecks it against a keyring. In an OCI registry the provenance file is stored as an extra layer. The Helm documentation also points to a helm-sigstore plugin, and because an OCI chart is a registry artifact, you can sign its digest with Cosign as you would an image and verify it in your pipeline or GitOps controller. Helm 4.3.0 addedSOURCE_DATE_EPOCHsupport, so the same chart packaged twice can produce the same archive and the same digest. Our drill confirmed this. - Kustomize: nothing to sign except the Git commit or the OCI artifact your controller pulls. Pin remote bases to a commit, not a branch.
- Timoni:
timoni mod push --sign=cosignsigns while publishing, keyless or with a key, andtimoni mod pull --verifychecks it. Notation is also supported. Keyless signing records the signer identity in the public Rekor transparency log, which the documentation warns about.
Secrets
- Helm: values passed to Helm, including
--setpasswords, are stored in the release Secret, together with any Secret manifests the chart renders. Treat read access to Secrets in a release namespace as read access to every value. Better patterns are an External Secrets Operator or a secret store CSI driver, where the chart references a secret name, or SOPS-encrypted values decrypted by your pipeline or controller. - Kustomize:
secretGeneratormakes Secrets from literals or files, which puts plain text in Git if you are careless. Flux can decrypt SOPS-encrypted files during reconciliation. Argo CD needs a plugin for the same. - Timoni: bundle values can come from cluster Secrets at apply time, from CI environment variables, from 1Password references or from SOPS files. The inventory Secret stores object IDs, not your values.
Ecosystem and day-to-day support
- Helm: almost every third-party product ships a chart. Artifact Hub indexes thousands. Every CI system, IDE and GitOps controller supports Helm.
- Kustomize: built into
kubectl, Argo CD and Flux. No separate install on most machines. Few projects publish Kustomize-only packages, but many publish plain YAML that works as a Kustomize base. - Timoni: a small catalogue of public modules. Works with Flux through documented patterns. For third-party software, you will still consume Helm charts.
What actually happens when you deploy
Following one change from a merge request to a running pod shows where the tools differ and where they do not.
- Pick the package. Helm resolves a chart version or digest from a repository or registry. Timoni pulls a module by version or digest from a registry. Kustomize reads folders from Git.
- Merge values. Helm layers values files and
--setflags over chart defaults. Timoni unifies CUE values, and conflicts are errors. Kustomize applies overlay patches in order. - Render. Helm runs Go templates and produces YAML text. Timoni evaluates CUE into objects. Kustomize builds the resource list and applies transformations.
- Validate. Helm checks
values.schema.jsonif the chart has one. Timoni checks types and, since 0.35.0, CRD schemas. Kustomize checks structure. Everyone should also run a server-side dry run or schema check in CI. - Apply. Helm 4 uses server-side apply for new releases. Timoni uses server-side apply in stages. Kustomize output goes to
kubectlor a controller. - Wait and record. Helm waits with kstatus if asked and stores a new release revision. Timoni waits by default and updates its inventory. Kustomize records nothing; Git is the record.
- Afterwards. None of the three keeps watching the cluster on its own. Drift detection and correction is the GitOps controller’s job, covered in the Argo CD and Flux post.
Steps 4 and 7 are where both Thrissur incidents happened. A different renderer in step 3 would not have prevented either.
A worked plan for the Thrissur team
Step 1: move to Helm 4 before Helm 3 support ends
Helm 3 receives security fixes only until 10 February 2027. The team adds a CI job that renders every chart with Helm 3.22.1 and Helm 4.3.1 and compares the output, then runs upgrades in staging with Helm 4. Things to check:
- Replace
--atomicwith--rollback-on-failureand--forcewith--force-replacein pipeline scripts. - Rewrite any post-renderer script as a Helm 4 plugin, or drop it.
- Change
helm registry logincalls to use only the registry host. - Decide on server-side apply. Existing releases stay on client-side apply after the switch. The team plans to move each release to
--server-sidedeliberately, one service at a time in staging, watching for field ownership conflicts with operators and with the horizontal pod autoscaler. - Tell both on-premises customers the date, since they run
helmthemselves.
Step 2: collapse 14 charts into one shared chart with a strict schema
Most of the 14 charts differ only by name, image, ports, resources and a few environment variables. The team builds one application chart with those as values, plus a small library chart for helpers that two unusual services need. It gets a values.schema.json with types, minimums and additionalProperties: false at each level, so replicaCont fails the pipeline instead of reaching production.
A trimmed example of the schema idea:
{
"type": "object",
"additionalProperties": false,
"required": ["image", "replicaCount"],
"properties": {
"replicaCount": { "type": "integer", "minimum": 2 },
"image": {
"type": "object",
"additionalProperties": false,
"required": ["repository", "digest"],
"properties": {
"repository": { "type": "string" },
"digest": { "type": "string", "pattern": "^sha256:[a-f0-9]{64}$" }
}
}
}
}
In CI, every merge request runs helm lint, helm template for each environment, a schema check of the rendered output against the cluster’s Kubernetes version, and posts the rendered diff as a comment. Reviewers read the YAML that will be applied, not only the template change.
Step 3: publish charts to an OCI registry, install by digest, and sign
- Package with
SOURCE_DATE_EPOCHset to the commit time so that the same commit always produces the same chart digest. - Push to the GitLab container registry with
helm push, next to the images. - Sign the chart digest with Cosign in CI using the pipeline’s OIDC identity, the same way the images are signed (see the image signing post).
- Deploy by digest, not by version, and verify the signature before install.
- Mirror the third-party charts they use into the same registry, pinned by digest, so a changed upstream chart cannot slip in under the same version number.
For the two on-premises customers, this replaces the emailed zip: they pull a specific signed digest, and the team can tell exactly what each customer runs.
Step 4: take secrets out of --set
Passwords move into the cloud secret manager, synced into the cluster by an External Secrets Operator. The chart only references the Kubernetes Secret name. For the on-premises installs, where there is no cloud secret manager, the customer’s admins create the Secret themselves from a documented list, and the chart refuses to render if the name is not set. Old release revisions still hold the old passwords, so the team rotates every password that was ever passed with --set.
Step 5: make drift visible before you fight it
The kubectl edit incident was a process gap, not a Helm gap. The team moves deployments from pipeline-driven helm upgrade to a GitOps controller, choosing between Argo CD and Flux as discussed in the earlier post. Until that is done, a nightly job runs helm diff upgrade for each production release and posts any difference to the team channel. The support runbook says: an emergency change made with kubectl must be followed by a merge request the same day.
Step 6: Kustomize for last-mile patches only
Kustomize stays useful in one place: small patches to third-party charts the team does not own, for example adding tolerations or a security context that the chart does not expose. They keep these in a Kustomize folder that inflates the chart and patches it, used through the GitOps controller. They do not rewrite their own services in Kustomize, because they need a package format for on-premises customers.
Step 7: a bounded Timoni pilot
One internal service, the job that sends appointment reminders by SMS, is packaged as a Timoni module and deployed to the dev cluster only, for two months. The pilot measures: how long it takes two engineers to become comfortable with CUE, how CRD validation and drift diffs feel in review, how upgrades go across three Timoni minor releases, and how it fits with the chosen GitOps controller. If the answer is good and Timoni reaches a stable API, the team can reconsider for new services. It does not touch the on-premises customers, who know Helm.
When Kustomize or Timoni is the better choice
- Choose Kustomize when you deploy only your own services to your own clusters, the differences between environments are small, and you already use Argo CD or Flux. Plain YAML with small overlays is the easiest thing to review and debug. It is also the natural way to patch third-party charts without forking them.
- Choose Timoni when your team is comfortable learning CUE, values mistakes and CRD mismatches are a real recurring cost, you want signing and digests built into the workflow, and you can accept pre-1.0 change and a small maintainer base. Platform teams that build internal modules for many product teams get the most from its types and bundles.
- Choose Helm when you distribute software to others, consume many third-party charts, or want the widest tooling support and a CNCF Graduated project with a published support policy.
- Consider cdk8s, KCL or Jsonnet with Tanka when your team already uses that language for other configuration, or when manifests are generated from larger programs. Each brings its own learning curve and a smaller community than Helm.
When not to change at all
- The problem is missing checks. A schema, a rendered diff in review and a server-side dry run fix more incidents than a new language.
- You ship to customers who know Helm. Asking them to learn a new tool is a real cost on their side.
- You are still on Helm 3. Move to Helm 4 first. Combining a major version change with a tool change doubles the risk.
- Nobody will own the new tool. A second packaging tool next to Helm means two sets of conventions, CI jobs and upgrade notes.
- The reason is a conference talk or someone else’s blog. Their charts are not your charts. Try the tool on one real service first.
A short history, so the choices make sense
Helm started at Deis and was shown at the first KubeCon in November 2015. Helm 2, built together with Google’s Deployment Manager team, introduced Tiller, a server component inside the cluster, which became a long-running security concern. Helm joined the CNCF in June 2018. Helm 3, in November 2019, removed Tiller and moved release records into Secrets in each namespace. Helm graduated in the CNCF in May 2020, added OCI registries as a stable feature in version 3.8, and released Helm 4 in November 2025.
Kustomize came from engineers at Google in 2018 as a reaction to templating: Kubernetes YAML should stay valid YAML, and customisation should be declarative patching. It joined kubectl in Kubernetes 1.14 in March 2019, though the embedded version stayed at 2.0.3 until kubectl 1.21. Kustomize 5.0 came out in February 2023.
Timoni’s first release was in February 2023. Its author, Stefan Prodan, had worked on Flux for years, and Timoni carries Flux ideas such as server-side apply with drift detection, OCI artifacts and Cosign verification. It chose CUE because CUE treats configuration as typed data that can be validated and merged, not as text to fill in.
The histories explain the designs. Helm grew as a package manager for distribution and carries a decade of compatibility. Kustomize grew as a rejection of templates and stays deliberately small. Timoni started from type safety and modern supply chain practice, and is still growing into a stable API.
Operational costs to plan for
- Helm: a major upgrade now, then regular minor upgrades; chart schema upkeep; release Secret growth and history limits; plugin hygiene, since plugins were where 2026’s serious advisories were; third-party chart mirroring and signing.
- Kustomize: keeping overlays small as environments multiply; the
kubectl-embedded version lagging the standalone binary; a separate solution for apply, pruning and rollback; secret handling outside Kustomize. - Timoni: CUE training and code review skills; upgrades across pre-1.0 releases that may change APIs; a smaller pool of examples and help online; still needing Helm for third-party software.
- All three: CI that renders and checks the output, a registry for artifacts, signing keys or identities, and a person who owns the packaging conventions.
A decision guide
- On Helm 3 today: upgrade to Helm 4 before 10 February 2027, then decide anything else.
- Many copied charts for your own services: one shared Helm chart with a strict schema.
- Own services only, small environment differences, GitOps already in place: Kustomize is a good fit.
- Third-party chart missing one option: Kustomize patch over the inflated chart, not a fork.
- Shipping software to customer clusters: Helm with OCI, digests and signatures.
- Platform team building typed internal modules, comfortable with CUE and pre-1.0 change: pilot Timoni.
- Hotfixes getting overwritten: GitOps controller and a runbook, whichever packaging tool you use.
Checklist before you template another YAML file
- The Helm version in CI and on every operator laptop is supported, with the Helm 3 end date in the calendar.
- Every chart has a values schema that rejects unknown keys and wrong types.
- Rendered manifests are shown in code review for every environment.
- Rendered output is checked against the target Kubernetes version before apply.
- Charts and modules are stored in an OCI registry, deployed by digest and signed.
- Third-party charts are mirrored and pinned by digest.
- No secret values are passed with
--set, and old ones have been rotated. - Drift is visible through a GitOps controller or at least a scheduled diff.
- CRD upgrades have a written procedure, separate from application upgrades.
- Rollback is tested, including what it does not roll back.
- One person owns packaging conventions and tool upgrades.
Common mistakes
- Blaming the template language for a missing check. Our drill showed
helm createcharts accept typos and wrong types until you add a schema. - Passing passwords with
--set. They live on in every release revision Secret. - Trusting version numbers for charts. A version tag in a repository can be replaced. A digest cannot.
- Forgetting that
helm templateincludes test hooks. In our drill, inflating a chart through Kustomize put the chart’s test Pod into the output as an ordinary resource. Check what you apply. - Assuming
kubectl apply -kuses the latest Kustomize. It uses the version built into yourkubectl. - Expecting rollback to undo data or CRD changes. None of these tools does that.
- Mixing tools without a rule. Helm for some services, Kustomize for others and a script for the rest makes every incident slower.
- Adopting a pre-1.0 tool for customer-facing installs. Pilot first, on something internal.
A small hands-on drill
We ran a small local drill on 27 October 2026, between about 6:20 AM and 6:23 AM IST, on a Linux 6.12 x86_64 test machine with no Kubernetes cluster. The only network use was downloading the official Helm 4.3.1, kustomize 5.8.3, Timoni 0.36.0 and kubectl 1.37.1 Linux x86_64 release files, each checked against its published SHA-256 checksum. We did not verify Timoni’s signed packslip, did not push anything to a registry, and did not install anything into a cluster.
What we saw:
- Versions.
kubectl1.37.1 reported an embedded Kustomize v5.8.1, while the standalone binary was v5.8.3. Timoni reported CUE 0.17.1 and APItimoni.sh/v1alpha1. - Helm without a schema. A chart made with
helm createrendered with--set replicaCont=5(a typo) without any error, leaving one replica. It also rendered--set replicaCount=threeasreplicas: threewithout complaint. - Helm with a schema. After adding a minimal
values.schema.jsonthat typesreplicaCountas an integer, the string value was rejected with “got string, want integer”. The typo was still accepted, because the schema did not setadditionalProperties: false. - Helm test hooks.
helm templateoutput included the chart’s test Pod with thehelm.sh/hook: testannotation. - Reproducible packages. Packaging the same chart twice, after touching one file, gave two different SHA-256 digests. With
SOURCE_DATE_EPOCHset to a fixed value, both packages had the same digest. - Kustomize overlay. A base with a Deployment and a generated ConfigMap, and a production overlay setting namespace, image tag, replicas, a label and a merged ConfigMap value, produced byte-for-byte the same output from
kustomize buildandkubectl kustomize. The ConfigMap got a hash suffix, and the Deployment’s reference to it was rewritten to match. - Kustomize with a Helm chart. A
helmChartsentry failed without--enable-helm. With it, Kustomize rendered the chart through Helm 4.3.1 and applied a JSON patch on top. The chart’s test Pod appeared in the output as an ordinary resource. - Timoni. A module created with
timoni mod initbuilt offline with production values. A misspelt values key failed with “field not allowed”, and a string for replicas failed with a type conflict.timoni apply --dry-run --diffneeds a real API server, so we did not run it.
This is not a benchmark, and nothing was installed or upgraded on a cluster. It shows three practical points. Helm’s safety depends on the schema the chart author writes. Typed tools such as Timoni catch the same mistakes by default. And what Kustomize or kubectl produces depends on versions and flags you should pin and check.
Unlearn, re-learn
Unlearn the idea that the packaging tool is the cause of most deployment problems. In our scenario, a typo, a hotfix and a password on the command line all slipped through because nothing checked them. Unlearn, too, the idea that templates are always bad or that types are always worth the learning curve; each choice moves the cost somewhere else. Re-learn packaging as a supply chain step: versioned artifacts in a registry, installed by digest, signed and verified, validated against a schema, reviewed as rendered YAML, and kept in sync by a controller that shows drift. Move off Helm 3 now, fix the checks you are missing, and then try Kustomize or Timoni on one real service where they clearly fit. The tool you already know, used with discipline, is often enough.
Sources
- Helm repository, releases, security advisories and release notes for v4.0.0, v4.3.0, v4.3.1 and v3.22.1
- Helm blog: Helm 4 released and Helm 3 end of life
- Helm documentation: Helm 4 overview, OCI registries, provenance and integrity, charts and chart hooks
- Helm on the CNCF site
- Kustomize repository and kubectl version notes, releases and documentation
- Timoni repository, releases, documentation, comparison with Helm and Kustomize, module signing, bundle secrets and timoni apply
- Kubernetes server-side apply and Kubernetes 1.14 release announcement
- Nearby tools: Helmfile, cdk8s, KCL, Tanka and Jsonnet
- CNCF landscape data for project maturity
Releted Posts
Self-hosted CI in 2026: revisit Jenkins, Woodpecker CI and Tekton before you add another build agent
Most teams that keep builds on their own servers did not choose their CI engine recently. It was chosen years ago, by someone who may have left, on a machine that has been upgraded “when there was time”.
Read moreContainer image signing in 2026: revisit cosign, Notation and in-toto attestations before you trust another image tag
Picture a software company in Rajkot that builds production planning and job work software for foundries, forging units and engineering workshops across Saurashtra (an invented example, not a real client).
Read moreContainer registries in 2026: revisit Harbor, Zot and CNCF Distribution before you push another image
Picture a product engineering team in Bengaluru that runs about forty microservices on Kubernetes for a B2B payments product used by NBFCs across Karnataka and Maharashtra (an invented example, not a real client).
Read more