A reusable Helm chart library for a platform team — a shared library chart of templated helpers, two opinionated application charts built on top of it, and a fully automated lint → test → release pipeline that publishes a versioned Helm repository to GitHub Pages.
graph TD
subgraph Library
C["common<br/>(type: library)<br/>labels · image · probes<br/>resources · securityContext"]
end
C --> WS["web-service<br/>(application)<br/>Deployment · Service · Ingress<br/>HPA · PDB · NetworkPolicy"]
C --> WK["worker<br/>(application)<br/>Deployment · PDB<br/>KEDA ScaledObject / HPA"]
WS --> CL[(Kubernetes<br/>clusters)]
WK --> CL
flowchart LR
PR["Pull Request<br/>(bump Chart.yaml version)"] --> LT["Lint & Test<br/>ct lint · helm unittest<br/>kubeconform · ct install (kind)"]
LT -->|required check| MG["Merge to main"]
MG --> CR["chart-releaser-action<br/>package + create GitHub Release"]
CR --> GP["gh-pages branch<br/>index.yaml = Helm repo"]
GP --> U["helm repo add acme …<br/>helm install"]
- Library chart for DRY templating —
commoncentralizes labels, fullname, image strings, probes, resources and hardened security contexts. App chartsincludethem instead of copy-pasting_helpers.tplinto every service. - Schema-validated inputs — each application chart ships a
values.schema.json, so bad values fail fast athelm install/linttime. - Automated SemVer releases —
helm/chart-releaser-actionpackages charts and publishes them to a GitHub Pages Helm repo; only versions not yet released are published. - CI lint & test gate —
ct lint,helm unittest,kubeconformmanifest validation, and a realct installagainst an ephemeralkindcluster, all required before merge. - Hardened defaults — non-root, read-only root filesystem, dropped
capabilities,
RuntimeDefaultseccomp, PDBs and autoscaling out of the box.
| Chart | Type | Purpose | Key resources |
|---|---|---|---|
common |
library | Shared named-template helpers reused by every app chart. | (renders nothing) |
web-service |
application | Stateless HTTP microservice. | Deployment, Service, Ingress, HPA, PDB, ConfigMap, NetworkPolicy, ExternalSecret |
worker |
application | Queue / async worker. | Deployment, PDB, KEDA ScaledObject (or custom-metric HPA) |
Add the published repository and install a chart:
helm repo add acme https://acme.github.io/helm-charts-library
helm repo update
# A web service
helm install my-web acme/web-service \
--set image.repository=ghcr.io/acme/web \
--set image.tag=1.4.2 \
--set ingress.enabled=true \
--set ingress.hosts[0].host=web.example.com
# A queue worker (scales to zero with KEDA)
helm install ingest acme/worker \
--set image.repository=ghcr.io/acme/worker \
--set image.tag=2.0.1 \
--set env.QUEUE_NAME=ingestOr develop locally from this repo:
make deps # pull the common library into the app charts
make lint test # ct lint + helm unittest
make template # render manifests- Open a PR that bumps the
version:in the affected chart'sChart.yaml(SemVer). Changingcommonis high-blast-radius and is gated byCODEOWNERS. - The Lint & Test workflow runs on the PR and is a required status check:
ct lint,helm unittest,kubeconform, andct installon akindcluster for any changed chart. - On merge to
main, the Release workflow runshelm/chart-releaser-action, which packages each chart, creates a GitHub Release with the.tgzasset, and updatesindex.yamlon thegh-pagesbranch — turning the repo into a consumable Helm repository. - Already-released versions are skipped, so unrelated merges don't re-publish.
Problem. A platform team supporting many microservices across multiple
clusters found that every service carried its own hand-rolled Helm chart. The
_helpers.tpl, label conventions, probe wiring and security contexts had quietly
diverged — so a hardening change (say, enforcing read-only root filesystems) meant
editing dozens of near-identical charts, and "near-identical" hid real drift.
Approach. Packaging was split into a shared common library chart and a
small set of application charts (web-service, worker) that consume it.
The library owns the cross-cutting templates exactly once; the app charts own
only what is genuinely service-shaped (a Service + Ingress for HTTP, a KEDA
ScaledObject for a queue worker). Inputs are constrained with
values.schema.json so misconfiguration fails at lint time rather than in a
cluster, and a CI gate (ct + helm unittest + kubeconform + a real kind
install) runs before anything merges. Releases are automated with
chart-releaser, giving every consumer immutable, SemVer-pinned chart versions
from a single Helm repo.
Outcome. New services start from a vetted chart instead of a copy-paste, a single library bump rolls a policy change out everywhere, and the gap between "what the chart says" and "what's running" closes — boilerplate and drift both go down while review focuses on the parts that actually differ.
This case study is a generalized account of the pattern; it names no employer, client, or proprietary system.
MIT © Muhammad Imad