Skip to main content
Cloud DevOps Solutions
Get a quote Talk to an engineer

GitOps / Helm / ArgoCD

Writing Helm Charts Your Team Can Actually Maintain

We write Helm charts that package your applications as versioned, configurable units: sensible templates, a documented values file, a JSON schema, health probes, resource settings and tests. The goal is a chart other teams can reuse without reading the templates.

Key takeaways

  • A good chart exposes a small, documented set of values and hides the Kubernetes boilerplate.
  • Schema validation and linting catch bad values before a deploy, not during one.
  • One shared chart for many similar services often beats a bespoke chart per service.
  • Charts are versioned artefacts with their own release cycle, separate from application versions.
  • Helm is not always the answer: plain manifests with Kustomize are better for simple, single-purpose apps.

What goes wrong with unmanaged Helm

Helm is easy to start and easy to misuse. A chart is generated with helm create, the default boilerplate is left in place, and every team then bolts on its own conditionals. A year later there are twelve charts that look similar, behave differently, and nobody dares upgrade. Values files have hundreds of lines, half of which do nothing.

We write Helm charts as a product: small interface, safe defaults, tested, versioned. It is a core part of our GitOps, Helm, ArgoCD and Flux service, and the output is consumed by your GitOps controller rather than run by hand.

What a good chart looks like

  • A values file a developer can understand in two minutes.
  • Defaults that are safe: resource requests set, probes defined, non-root user, read-only root filesystem where the app allows it.
  • A values.schema.json that rejects typos and wrong types.
  • Consistent labels and names so dashboards, alerts and kubectl filters work the same everywhere.
  • Upgrades that do not surprise you: no hidden changes to selectors or immutable fields.

How it works in practice

The values interface

We start from what developers need to change, not from everything Kubernetes can express. For a typical web service that is image and tag, replica count or autoscaling bounds, resource requests and limits, environment variables, ingress host, and a few feature flags. Everything else gets a sensible default.

# values.yaml (excerpt)
image:
  repository: registry.example.com/payments/api
  tag: ""            # set by the pipeline, defaults to .Chart.AppVersion
replicaCount: 2
resources:
  requests:
    cpu: 100m
    memory: 256Mi
  limits:
    memory: 512Mi
autoscaling:
  enabled: false
  minReplicas: 2
  maxReplicas: 6
  targetCPUUtilizationPercentage: 70
ingress:
  enabled: true
  host: payments.example.com

Setting resource requests and limits by default matters more than it looks: workloads with none are the first to be evicted and the hardest to schedule well.

Schema validation

A JSON schema stops mistakes at install time. Helm validates values against values.schema.json automatically on install, upgrade, lint and template:

{
  "$schema": "https://json-schema.org/draft-07/schema#",
  "type": "object",
  "required": ["image"],
  "properties": {
    "replicaCount": { "type": "integer", "minimum": 1 },
    "image": {
      "type": "object",
      "required": ["repository"],
      "properties": {
        "repository": { "type": "string" },
        "tag": { "type": "string" }
      }
    }
  }
}

This catches a quoted number where an integer was expected, or a misspelt key, long before it silently does nothing in production.

Helpers and consistency

Named templates in _helpers.tpl generate names, labels and selectors once. Selectors are the dangerous part: a Deployment selector is immutable, so a label change in a helper can break upgrades. We keep selector labels minimal and stable, and put everything else in ordinary labels.

Library charts for fleets of similar services

If you run thirty services that are all variations of “HTTP API with a database”, thirty copies of nearly identical templates is a liability. A library chart holds the shared logic. Each service chart becomes a few lines plus its own values. Fixing a probe default or adding a security setting is then one change, released as a new library version.

Testing and upgrade safety

Our standard pipeline for a chart:

  1. helm lint with strict mode on each values file.
  2. helm template for every environment, piped to kubeconform for schema checks and to a policy tool such as kube-linter for common mistakes.
  3. Unit tests for templates with conditionals using helm unittest.
  4. In CI, install into a throwaway cluster with chart-testing and run test hooks.
  5. A rendered diff posted to the pull request, so reviewers see what the change does to the manifests, not just to the templates.

That last step is the most valuable. Reviewing template changes is hard; reviewing the YAML they produce is easy.

Helm inside GitOps

With ArgoCD, Helm is a templating step: the controller renders the chart and applies the result, so helm list will not show the release. With Flux, a HelmRelease runs a real Helm install and keeps release history. Both approaches work, but they behave differently around hooks, rollbacks and drift, so we design the chart with the controller in mind. See how this connects to promotion across environments and publishing charts.

Decisions and trade-offs

Many values or few. Every option you expose is something you must document, test and support. We expose less than clients initially ask for, and add options when a second real use appears.

Subcharts and dependencies. Bundling a database as a subchart is convenient for demos and risky for production, because the lifecycle of the data is tied to the application release. We usually deploy stateful dependencies separately.

Hooks. Helm hooks for migrations or jobs can behave unexpectedly under GitOps controllers. Where possible we run migrations as explicit, observable jobs and design hooks carefully.

When not to write a chart. For a one-off internal tool deployed to a single environment, a handful of YAML files with Kustomize is easier to read. Do not add templating until you have something to template.

Common failure modes

  • Templating everything. Charts full of if and with become unreadable. Keep logic small.
  • Latest tags. Mutable tags make rollbacks impossible. We pin by version or digest.
  • No rollout settings. Missing probes and disruption budgets cause outages during upgrades. Defaults include them.
  • Secrets in values files. Never. Use Sealed Secrets or External Secrets Operator.
  • Unversioned changes. A chart changed without a version bump deploys silently different behaviour. CI enforces version bumps.

Delivery and handover

You receive the charts in your repository, with schema, tests, README generated from values, and example values for each environment. Your developers make a change themselves during handover, usually adding a new service from the library chart, so the process is understood rather than inherited.

How to tell it is working

New services are a short values file rather than a copy of an old chart. Upgrades of the chart show a clear diff and rarely surprise anyone. Developers stop asking for help with YAML. If your charts have grown unruly, contact us for a review.

References

How we deliver it

  1. 01

    Audit current manifests

    We review your existing YAML, scripts and any third-party charts, and decide what should become a chart and what should not.

  2. 02

    Design the values interface

    We define the small set of knobs developers actually need, with defaults that are safe in production.

  3. 03

    Write templates and helpers

    Templates cover workload, service, ingress, config, autoscaling and disruption budgets, with consistent labels and naming helpers.

  4. 04

    Add schema and tests

    A values.schema.json, helm lint, template rendering checks and in-cluster test hooks guard against regressions.

  5. 05

    Wire into CI and GitOps

    Charts are packaged, versioned and published, and consumed by ArgoCD or Flux with per-environment values.

  6. 06

    Document and train

    A short README, example values and a walkthrough let your developers change and extend the chart safely.

Tools and technologies

Helmhelm linthelm templatehelm unittestchart-testing (ct)kubeconformkube-linterhelm-docsKustomizeArgoCDFlux

Frequently asked questions

Should we write our own Helm chart or use a community one?
Use a maintained community chart for commodity software such as databases, ingress controllers or monitoring, and pin its version. Write your own chart for your applications, where you control the structure and release cadence. Forking a community chart for light customisation tends to become a maintenance burden, so we prefer wrapping it with values.
Helm or Kustomize?
Helm suits parameterised, reusable packages with release semantics. Kustomize suits patching plain YAML with environment differences. They combine well: a chart for the application, and Kustomize overlays or values files per environment. For a single-purpose internal service with two environments, plain Kustomize can be simpler.
What is a library chart?
A library chart contains reusable template definitions but deploys nothing by itself. Application charts depend on it and call its templates, so deployment, service and ingress logic is written once. It suits organisations with many similar services that need consistent labels, probes and security settings across all of them.
How do you test Helm charts?
We run helm lint, render with helm template for each values file, validate output against Kubernetes schemas with kubeconform, and run unit tests with helm unittest. In CI, chart-testing installs the chart into a temporary cluster. This catches broken templates and unsafe defaults before they reach any real environment.
Can you convert our existing YAML into a chart?
Yes. We group the manifests by application, extract only what actually varies into values, and keep the rest fixed. The aim is not to template everything but to make the right things configurable. We verify the rendered output matches what is running today before switching a deployment over.

Talk to an engineer about helm chart development

Book a free 30-minute call. We review your current setup and tell you honestly whether and how we can help.

Written by Saddique Khan, DevOps / SRE / Cloud Consultant Engineer. Last updated 2026-10-08.