Why?
Rationale
- Best practices (fits 80/20 of use cases)
- Convention over configuration (sane defaults)
- Configuration over code (don't repeat yourself)
Features
Workloads
- Multiple named Deployments, Services, Ingresses, CronJobs, Jobs and PVCs, all in one chart
- Single-resource shorthands (
deployment:,service:,ingress:) for simple single-app deployments - Bare keys for Deployments, CronJobs and Jobs produce a working resource with no configuration needed
- HPA (autoscaling/v2) with shorthand CPU/memory metrics or full metrics list
- Ingress TLS defaults:
tls: trueauto-derives hosts andsecretNamefrom the ingress; list and dict modes fill in any omitted fields
Configuration management
variablesstored in a ConfigMap,secretsin a Secret,sealedSecretsin a SealedSecret, all auto-mounted as environment variablesconfigure.*flags per container: opt-in or opt-out of any auto-behaviour (variables, secrets, files, persistence) at the global, pod, or container level- Both list-style and dict-style
env,envFrom, andports: dict keys become thenamefield automatically - Automatic pod rollouts on ConfigMap/Secret changes via checksum annotations
Files and persistence
- Mountable config files (
mountedFiles), secret files (mountedSecretFiles), and sealed secret files (mountedSealedSecretFiles) - PVCs with optional auto-mount; reuse existing claims via
claimName - Bitnami Sealed Secrets support for both env vars and file mounts
Containers
- Init containers and sidecar containers with opt-in
configureflags (no accidental secret leakage to utility containers) - Per-container
securityContext,resources,healthChecks,command,args,imagePullPolicy - Health checks at
/healthz/readyand/healthz/liveby convention
What makes this chart different
Native Kubernetes YAML as values
Fields like healthChecks, securityContext, affinity, volumes, resources, and ports accept standard Kubernetes YAML directly: you write exactly what goes into the manifest, with no chart-specific schema to learn. Every Kubernetes feature works immediately, without waiting for a chart update to expose a new probe type, security flag, or scheduling option. Charts that define their own abstraction schema for every field (200+ parameters) are harder to learn, inevitably lag behind the Kubernetes API, and break when you need something they haven't modelled.
configure.* flags
Each automatic behaviour has its own configure.* flag. Main containers receive env, variables, secrets, and mounted files by default and a PVC mount only with configure.persistence: true; any flag can be switched off per pod or per container. Init and sidecar containers receive nothing unless their own configure block turns it on, so a log shipper sidecar never gets database credentials by accident.
Multiple workloads
deployments:, services:, ingresses: are maps: define a frontend, an API, and a worker in a single values.yaml with shared config (image, secrets, variables) and per-resource overrides.
Sealed Secrets
The chart supports Bitnami Sealed Secrets natively, for both environment variables and mounted files: secrets live encrypted in the Git repository and are decrypted only inside the cluster, so secret management stays GitOps-safe.
extraManifests
Any Kubernetes resource the chart doesn't natively generate (NetworkPolicies, ExternalSecrets, OpenShift Routes, cert-manager Certificates, CRDs) can be declared inline in extraManifests. Dict mode auto-injects name, namespace, and labels; list mode renders each entry as written. Neither needs a fork of the chart.
Dict and list styles
Standard Kubernetes YAML uses lists for env vars, ports, and volumes, which is verbose and repetitive for simple cases. This chart accepts both styles for every list field, and promotes dict keys to name fields automatically:
# Kubernetes list style: always works, familiar to anyone who knows K8s
env:
- name: DATABASE_HOST
value: postgres
- name: DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: db-secret
key: password
# Dict style: concise for simple values, full objects still supported
env:
DATABASE_HOST: postgres
DATABASE_PASSWORD:
valueFrom:
secretKeyRef:
name: db-secret
key: passwordThe same applies to ports, envFrom, volumeMounts, and extraManifests. You can mix styles freely. Dict style cuts a simple entry from three lines (- name: / value:) to one, with no new abstraction to learn.
Application charts
Every chart in this repository (archivebox, bookstack, akeneo, and others) is an application-specific chart that lists standard as a dependency and sets opinionated defaults under the standard: key. Operators override only what they need in their own values file, without forking or patching the chart.
This is the same problem Kustomize solves (apply environment-specific overrides to a shared base), but with Helm's full templating and the Helm release lifecycle (diff, rollback, hooks): the application chart plays the role of Kustomize's base, and each environment's values.yaml the overlay.
# charts/myapp/values.yaml: opinionated defaults shipped with the chart
standard:
image:
repository: myorg/myapp
variables:
APP_ENV: production
services:
myapp:
ports:
http:
port: 80
deployments:
myapp:
configure:
persistence: true# deploy/staging/values.yaml: operator overrides only what differs
standard:
variables:
APP_ENV: staging
image:
tag: "abc123"Bare keys
A deployment, cron job or job entry with no value renders a working resource from the global defaults, with no required fields and no empty {} block.
Helm criticisms
Several common complaints about Helm are about badly written charts rather than Helm itself. Each one below comes with what this chart does about it.
"Go templating in YAML is too complex."
As a chart user you never write Go templates. You write plain YAML values files. The templating lives inside the chart, and make tests checks its output against golden manifests.
"Minor value changes can unexpectedly enable hidden chart features."
Every automatic behaviour sits behind a documented configure.* flag (see Values). Main containers receive env, variables, secrets, and mounted files unless a flag turns them off, and a PVC mount only when configure.persistence is true. Init and sidecar containers receive none of it until they opt in.
"It's hard to see what the final YAML will look like before deploying."
Set debug: true to render the chart with internal state visible as comments. Use helm template to preview the full output locally before any cluster interaction. The base-chart + values overlay pattern also makes changes predictable: the application chart defaults are stable; your values file is the only moving part.
"Chart quality varies; you end up forking or patching."
The base-chart dependency pattern means you never fork. Your application chart inherits from standard, sets its own defaults, and operators override only what they need. Rebuilding the application chart's dependencies (helm dependency build) picks up changes to standard. The extraManifests escape hatch covers one-off resources the chart doesn't natively support, so those need no fork either.
"Helm is overkill for simple apps."
A working deployment, service, and ingress takes four lines of values:
deployment:
service:
ingress:
hosts: [myapp.example.com]For a single app that's about as short as a docker-compose.yaml, and the deployments: map takes over once there's more than one workload.