Helm Library Charts: Stop Copy-Pasting the Same Templates
Every service chart in your repo has the same Deployment, Service, and HPA boilerplate. Helm library charts let you define that logic once and import it everywhere.
- #kubernetes
- #helm
- #library-charts
- #templates
- #dry
I once inherited a monorepo with eighteen Helm charts, and seventeen of them had the exact same 90-line _helpers.tpl and a near-identical deployment.yaml. When we needed to add a single security context field to every service, I changed it eighteen times and still missed one — which I discovered three weeks later when that service failed a pod security check in staging. That afternoon I learned about Helm library charts, and most of that duplication evaporated.
A library chart is Helm’s answer to “define your templating logic once, use it from many application charts.” It’s underused, slightly awkward to set up the first time, and genuinely worth it once you have more than two or three charts that look alike.
What makes a chart a library chart
The mechanical difference is one line in Chart.yaml:
apiVersion: v2
name: common
type: library
version: 1.0.0
type: library tells Helm this chart cannot be installed on its own and renders nothing by itself. It exists purely to export named templates that other charts call. Compare that to a normal type: application chart, which is the default and produces installable manifests. A library chart’s templates/ directory contains only define blocks — no top-level YAML that would render directly.
common/
Chart.yaml
templates/
_deployment.tpl
_service.tpl
_helpers.tpl
The leading underscore on those filenames matters: Helm treats _*.tpl files as partials and never renders them as manifests. Everything inside is a named template waiting to be invoked.
Defining a reusable template
Here’s the core idea. In the library chart, you define a complete Deployment as a named template that reads values from the calling chart’s context:
{{- define "common.deployment" -}}
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "common.fullname" . }}
labels:
{{- include "common.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount | default 2 }}
selector:
matchLabels:
{{- include "common.selectorLabels" . | nindent 6 }}
template:
metadata:
labels:
{{- include "common.selectorLabels" . | nindent 8 }}
spec:
securityContext:
runAsNonRoot: true
seccompProfile:
type: "RuntimeDefault"
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
ports:
- containerPort: {{ .Values.service.port | default 8080 }}
{{- end -}}
That securityContext block is the whole point. Define it once here, and every service that imports this template inherits it. The miss-it-in-one-place problem disappears.
Wiring it into an application chart
An application chart declares the library as a dependency:
# myservice/Chart.yaml
apiVersion: v2
name: myservice
type: application
version: 0.1.0
dependencies:
- name: common
version: 1.0.0
repository: "file://../common"
Then run helm dependency build to vendor it into charts/. Now the application chart’s own templates shrink to almost nothing:
# myservice/templates/deployment.yaml
{{- include "common.deployment" . -}}
That single include line renders the entire shared Deployment, using this chart’s values.yaml for the image, replica count, and port. A new service chart becomes a Chart.yaml, a values.yaml, and a handful of one-line includes.
Pro Tip: Library templates receive the calling chart’s root context (.), so .Values, .Chart, and .Release all refer to the consumer, not the library. This is exactly what you want, but it means a typo in the consumer’s values.yaml surfaces as a confusing error inside the library template. Always test renders with helm template myservice/ before you trust a new library function.
Handling overrides cleanly
The reusable template shouldn’t be rigid. The trick is to make fields read from values with sensible defaults so any consumer can override just the parts it cares about:
resources:
{{- toYaml (.Values.resources | default dict) | nindent 12 }}
A consumer that wants custom limits sets them in its values.yaml; one that doesn’t gets an empty map and the cluster defaults. For genuinely structural differences — say one service needs an extra init container — you can expose an optional merge point and let the consumer pass a snippet through values, or keep that one chart’s deployment.yaml hand-written rather than fighting the abstraction. Not everything belongs in the library, and forcing it there is how library charts get a bad reputation.
Let AI handle the tedious extraction
Refactoring eighteen near-identical charts into one library is exactly the kind of mechanical, high-volume work an AI assistant is good at. I’ll paste two or three of the existing deployment.yaml files and ask the model to diff them, identify the truly common structure, and produce a parameterized library template plus the values keys each consumer would need. It’s fast and accurate at spotting that fourteen charts share a block and four diverge in one field.
The mental model I keep is that the assistant is a quick junior engineer: brilliant at the first-pass extraction and the boring consistency sweep, but not the one who runs helm upgrade. Everything it generates here templates manifests that will eventually mutate the cluster, so it stays human-in-the-loop — I read every rendered output myself, and the model never gets a kubeconfig or any production credential. It works on chart text only. I run the actual helm template and helm upgrade --dry-run locally. For structuring that review there’s a workflow in the code review dashboard, and reusable starting prompts in the prompt library and curated prompt packs.
A prompt that earns its keep:
Here are three Helm deployment.yaml templates from different service charts.
Produce: (1) a single library template "common.deployment" capturing the
shared structure with values-driven defaults, (2) the list of values keys
each consumer must provide, (3) which fields diverge and should stay
per-chart. Output templates only — no helm or kubectl commands.
Versioning the library like real software
Because consumers pin a version in their dependencies, your library chart is now an internal dependency with the same upgrade discipline as any other. Bump the version on every change, treat template signature changes (renaming a values key) as breaking, and roll consumers forward deliberately. Publish it to a chart repository (an OCI registry works well) once it stabilizes, so charts in other repos can depend on oci://registry.internal/charts/common instead of a fragile relative path.
helm package common/
helm push common-1.1.0.tgz oci://registry.internal/charts
A useful discipline is keeping a short CHANGELOG in the library chart and a single example consumer chart in the same repo that exercises every exported template. That example doubles as a smoke test: a helm template example/ in CI catches a broken library function before any real service pins the bad version. Without it, you find out a renamed values key broke things when a downstream team’s deploy fails — which is exactly the cross-team friction library charts are supposed to remove. Treat the example consumer as part of the library’s contract, not an afterthought.
Wrapping up
Library charts trade a little upfront setup for a lot less copy-paste and a single place to enforce things like security contexts and labels across every service. Define the shared logic once with values-driven overrides, version it like the dependency it is, and let an AI assistant do the grunt work of extraction while you keep your hands on every render and every upgrade. Your future self, adding one field instead of eighteen, will thank you. More Helm patterns live in the Kubernetes & Helm guides.
Download the Free 500-Prompt DevOps AI Toolkit
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.