Roadmap
Items that say "Breaking change" need existing values files or behaviour updated when they land.
Standard
StatefulSet and DaemonSet support
The single largest missing feature. Deployments alone cannot cover:
- StatefulSet: stable network identity and ordered scaling required by databases, message queues, Elasticsearch, Redis clusters, and any application that uses
volumeClaimTemplatesfor per-pod storage - DaemonSet: one pod per node, required for log shippers, monitoring agents, CNI plugins, and node-level storage drivers
The natural design is a workloadType: field per entry, defaulting to Deployment:
deployments:
postgres:
workloadType: StatefulSet
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: [ReadWriteOnce]
resources:
requests:
storage: 10Gi
fluentd:
workloadType: DaemonSetStatefulSet-specific additions needed alongside this:
updateStrategy(vsstrategyon Deployment)podManagementPolicy(OrderedReady vs Parallel)volumeClaimTemplatesserviceNamefor headless service binding- Headless service support (
clusterIP: None) inservices:
Template fixes and inconsistencies
- Wire top-level
portNumberandhealthChecksshorthands into the default container of thedeployment:shorthand;values.yamldocuments both, but no template reads them (top-levelresourcesalready applies to every container) - Remove
metadata.namefrom Job pod templates: Kubernetes ignores pod template names in Jobs, it is unnecessary clutter - Remove the
_port.tplstub or implement it: no template includesstandard.containerPortorstandard.servicePort - Add
values.schema.json: typos in values keys are silently ignored; schema validation would catch them athelm installtime - Add missing checksum annotation for
mountedSealedSecretFilesin deployment pod annotations: changes to sealed secret files do not trigger a pod rollout
Configuration improvements
- Template rendering (
tpl) on an enumerated set of values, so values can reference other values (DATABASE_HOST: "{{ .Release.Name }}-postgres"): the metadata channels (labels,annotations,podLabels,podAnnotationsand their per-resource variants), ingresshostsandtls.secretName,env/variables,command/args. Excluded by design:mountedFiles/mountedSecretFiles(payloads legitimately contain foreign Go-template or Jinja braces),secrets/sealedSecrets, and structural keys the templates compute with (ports,replicaCount, names). Costs to accept:tplre-parses per call, and template strings in wrapper charts evaluate against the subchart's scoped values existingSecretpattern: allow referencing a pre-existing Secret by name instead of managing secret values through Helm (plays well with External Secrets Operator, Vault, and avoids values in release history)env/envFrommerging: first match wins (container overrides pod overrides global); support additive merging so global base env vars can be combined with per-container additionsenv.valueFrom/ downward API: document and testvalueFrom.fieldRef(pod name, namespace, node name) andvalueFrom.resourceFieldRef(CPU/memory limits as env vars); these work in list-style env but are undocumented and untested- Per-resource
variables,secrets,mountedFiles, etc.: allow scoping config to a specific deployment, cronJob, or job with resource-unique names (e.g.{fullname}-{resourcename}-variables) so different workloads in the same release can carry their own isolated configuration without sharing the global ConfigMap/Secret - Hook-phase config availability:
pre-install/pre-upgradehook jobs cannot consume the globalvariablesConfigMap orsecretsSecret: those are normal release resources, and Helm applies them after the pre-hook phase has already run and completed. On a fresh install the config does not exist yet (the hook job fails); on a later upgrade the previous release's config is still present but stale, which is worse because it fails silently. The fix is opt-in, hook-scoped config that lives only for the job's lifecycle:- When a job is a
pre-*hook and consumes config (configure.variables/secrets, or an explicit flag), emit a temporary ConfigMap/Secret annotated as the same hook event with a lowerhook-weightthan the job, so Helm applies it before the job in the same phase. - Give it
hook-delete-policy: hook-succeeded(plusbefore-hook-creation) so the temporary resource is cleaned up after the job runs and does not linger or collide with the global config. - Scope it to the job (
{fullname}-{jobname}-variables) to avoid clashing with the release's own ConfigMap/Secret. Pairs naturally with per-resourcevariables/secretsabove: a hook job usually wants its own config, not the shared global one.
- When a job is a
podAntiAffinityshorthand: the most common affinity use case (spread pods across nodes/zones) requires a verbose block; apodAntiAffinity: soft | hardshorthand would cover 90% of cases- Support
service.port: 80scalar shorthand that expands intoservice.ports.http.port: 80automatically workingDiron containers: set the working directory inside the container; some applications (Python, Node.js) require this to match the application rootimageas string or dict on all container types:initContainers,sidecarContainers, and namedcontainersrequire a full image reference string. Support a dict form that is merged with the globalimage:so only the differing fields need to be specified:yamlThe dict form uses the same fields as the top-level# Current: full string required initContainers: init-data: image: busybox:1.36 # Proposed: string still works initContainers: init-data: image: busybox:1.36 # Proposed: dict merged with global image: initContainers: init-data: image: name: busybox tag: "1.36" # registry and repository inherited from global image:image:block (registry,repository,name,tag) and is shallow-merged so only the specified keys override the global values.- Fix
test-connection.yamlto use the service'sresourcefullnameinstead of the globalfullname, and make the test image configurable instead of hardcodingbusybox imagePullSecretsdict mode: allow specifying registry credentials directly in values and have the chart generate thekubernetes.io/dockerconfigjsonSecret automatically, instead of requiring users to pre-create the secret out of band:yamlThe generated Secret name is then injected into the podimagePullSecrets: my-registry: registry: registry.example.com username: myuser password: mypasswordimagePullSecretslist automatically. The existing list-of-name-references behaviour is preserved for referencing pre-existing secrets.
Security defaults
Harden the default securityContext to match modern best-practice baselines. Stakater defaults to readOnlyRootFilesystem: true and runAsNonRoot: true; this chart defaults to empty {}. The right posture is secure-by-default with explicit opt-out:
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
runAsNonRoot: true
capabilities:
drop: [ALL]Breaking change for applications that write to the filesystem or run as root; needs a migration note and a clear securityContext: {} opt-out path.
Missing Kubernetes features (high value)
podDisruptionBudget:minAvailable/maxUnavailableto protect against node drains taking down all pods simultaneouslylifecycleHooks:preStop/postStarthooks on containers; thepreStop: sleep Npattern is essential for graceful shutdown behind cloud load balancersterminationGracePeriodSeconds: expose at pod level alongsidelifecycleHookstopologySpreadConstraints: spread pods across zones and nodes; the modern replacement for pod anti-affinity rulesrevisionHistoryLimit: control how many old ReplicaSets Kubernetes retains (default 10 adds noise; most charts use 3)startupProbeas an explicit sibling toreadinessProbeandlivenessProbeinhealthChecks: essential for slow-starting applications (JVM, large Python apps) to avoid liveness probes killing a legitimately slow boot
Missing Kubernetes features (medium value)
networkPolicy: NetworkPolicy resource gated behindnetworkPolicy.enabledpriorityClassName: assign PriorityClass to pods for eviction ordering under node pressureminReadySeconds: delay marking a pod available until it has been running N seconds; guards against crashlooping pods receiving traffic immediatelyautomountServiceAccountToken: falseas the default on ServiceAccount: more secure default aligned with hardened chartshostAliases: inject/etc/hostsentries into pods for split-horizon DNS or local overridesdnsPolicy/dnsConfig: custom DNS settings for pods that need non-default resolutionexternalTrafficPolicyon Services: controls whether NodePort/LoadBalancer preserves source IP (LocalvsCluster); important for security auditing and rate limiting by real client IPsessionAffinityon Services:ClientIPstickiness for WebSocket connections and session-heavy applicationsshareProcessNamespace: lets sidecar containers inspect the main container's process tree; useful for signal forwarding and debug sidecars- Headless services (
clusterIP: None) and ExternalName services: headless is required for StatefulSets to get stable per-pod DNS; ExternalName aliases external services inside the cluster - ExternalName service type:
type: ExternalNamewith anexternalNamefield that creates a CNAME DNS alias to an external hostname; useful for abstracting external databases, third-party APIs, or cross-namespace service references behind an in-cluster DNS name. Requires skippingportsandselectorin the rendered spec and suppressing the test-connection pod for that service:yamlservices: external-db: type: ExternalName externalName: postgres.example.com
Velero backup schedules
Velero integration following the same pattern as cronJobs:. Named entries generate Velero Schedule CRs, with sane defaults and a bare-key convention:
backups:
daily: # bare key → uses all defaults
weekly:
schedule: "@weekly"
ttl: 2160h # 90 days
storageLocation: offsite
database:
schedule: "0 1 * * *"
includedResources:
- persistentvolumeclaims
labelSelector:
app.kubernetes.io/component: databaseDefaults:
schedule: "@daily"ttl: 720h(30 days)storageLocation: defaultlabelSelector: the chart's own selector labels so only this release's resources are included
Note: Velero can also be configured at the server level to back up all PVCs automatically without any per-chart config. The backups: feature is for releases that need explicit schedules, custom retention, or multiple backup destinations.
Ecosystem integrations
- Full observability stack: when
metrics.enabled: true, generate the complete set alongsideServiceMonitor:PrometheusRule(alerting rules),GrafanaDashboard, andAlertmanagerConfig; stakater generates all four as a unit which is more useful than ServiceMonitor alone - ExternalSecrets:
ExternalSecretresource as an alternative to Sealed Secrets for teams using Vault, AWS Secrets Manager, GCP Secret Manager, or Azure Key Vault via the External Secrets Operator; Sealed Secrets stays for GitOps-pure setups, ExternalSecrets for operator-based workflows - KEDA:
ScaledObjectresource generation as an alternative to HPA; enables event-driven autoscaling on queue depth, Kafka lag, cron schedules, and custom metrics without requiring users to write the CRD by hand global.imageRegistry/global.imagePullSecrets: standard Bitnamiglobal.*keys so this chart works correctly as a sub-chart in umbrella deploymentsdiagnosticMode: override pod command withsleep infinityfor live debugging without changing real configuration; Bitnami pattern- RBAC: optional
Role/RoleBinding/ClusterRole/ClusterRoleBindinggeneration tied to the chart's ServiceAccount cert-managerCertificate resource: generate acert-manager.io/v1 CertificateCR directly (not just an Ingress annotation) for wildcard certificates, multiple SANs, or certificates used outside Ingress; gated behindcertManager.enabled: true
Future / long-term
- Networking platform abstraction: a
networking.type: Ingress | HTTPRoutefield that switches which resource type is generated; allows Gateway API migration without restructuring values; OpenShift Routes are intentionally out of scope (they can be added viaextraManifests) - Canary and blue-green deployments: patterns for Argo Rollouts or Flagger integration via
extraManifestsor dedicated annotation shortcuts - Vertical Pod Autoscaler (VPA): complement to HPA for right-sizing resource requests; gated behind
autoscaling.vpa.enabled
Template rendering in values
Support Go template expressions inside string values so users can reference release metadata or other values without hardcoding them:
variables:
APP_URL: "https://{{ .Release.Name }}.example.com"Values containing template expressions would need to be explicitly opted in (e.g. via a tpl: prefix or a dedicated tplValues: block) to avoid breaking existing plain-string values.
Documentation
The goal is documentation that exceeds bjw-s/app-template and Bitnami (both rated Excellent in the comparison table). Parity items close the gap; differentiator items go beyond what those charts offer because they cover features unique to this chart.
Parity
- Values reference table: every configurable key with type, default, and description in one scannable table; the first thing users look for and currently missing entirely
- Getting started guide: walks from zero to a running deployment (add repo → write a minimal
values.yaml→ install → verify); the current Install section is just two shell commands - Worked examples for common application types, at minimum: simple web app (image + service + ingress + TLS), app with secrets, multi-workload (API + worker), app with persistence; showing realistic complete
values.yamlfiles values.schema.json: enables IDE hover tooltips and inline validation when writing values files; turns passive docs into active docs (also listed under Template fixes)UPGRADING.md: breaking changes listed per version, separate from the CHANGELOG; operators need a quick reference for what to update on each bump- Expand
tests/README.md: how to run tests (make tests,make test TEST=name), how golden-file comparison works, how to add a new test case - Add
CONTRIBUTING.md: how to add a new template feature end-to-end, coding conventions, test requirements, commit message style
Differentiators
configure.*guide: explain all six flags, how they cascade (global → pod → container), why init/sidecar containers are opt-in, before/after examples of enabling per container; unique to this chart and currently undocumented- Multi-workload cookbook: dedicated guide for defining frontend + API + worker + shared secrets in one
values.yaml; unique feature with no equivalent in competing charts - Environment and secrets comparison: when to use
envvsvariablesvssecretsvssealedSecretsvsenvFrom; trade-offs around GitOps safety, Helm history exposure, and external operator compatibility - Sealed Secrets guide: walk through the flow (generate key → seal a secret → deploy → verify); cover both env-var and file-mount patterns; unique feature
- Base chart authoring guide: how to build an app-specific chart on top of
standard; coverChart.yamldependency declaration, nesting config understandard:, settingchartName/version, and what belongs in chart defaults vs operator config; the existing charts in this repository (archivebox, bookstack, akeneo, etc.) are the reference implementations - GitOps deployment guide: complete ArgoCD/Flux setup with Sealed Secrets, per-environment values files, and the
releaseNameoverride pattern - GitLab CI/CD guide: deploying with
helm upgrade --installin GitLab pipelines; environment-specific values files, review app patterns, and integration with GitLab Environments - GitHub Actions guide: deploying with
helm upgrade --installin GitHub Actions; OIDC-based cluster auth, environment-specific values files, and PR preview deployments - Datadog guide: enabling APM injection, log collection, and unified service tagging via
podAnnotationsandpodLabels - OpenTelemetry guide: sidecar collector pattern using
sidecarContainers:with opt-inconfigureflags; covers trace export, resource attributes, and collector config as a mounted file - Velero backup guide: documenting pod annotations for file-system backup and when server-side Velero auto-backup is sufficient (already started in README)
debug: truereference: what it renders, when to use it, and how to read the output; currently undocumented despite being the primary troubleshooting tool- Architecture overview: template hierarchy (
_helpers.tpl,_pod.tpl,_container.tpl), how context is passed between templates, how the configure inheritance chain works; useful for contributors and chart authors
Application charts
A sweep across the charts in charts/ that build on standard; the chart's own defaults are tracked under Standard above.
- Configure
healthCheckson the charts that ship without probes: akeneo, archivebox, bookstack, cachet, elasticsearch, hello-kubernetes, solr, valkey, yopass - Harden
securityContexton the charts that ship without one (bookstack, cachet, elasticsearch, hello-kubernetes, solr, valkey, wikijs, yopass) and audit the rest against the secure-by-default baseline - Publish a supported Kubernetes version matrix for every chart in
charts/,standardincluded; no chart declareskubeVersion, so the tested range has to be established before it can be documented - Chart the applications simple enough for it on
standard, so their values match every other application andmake installreaches them: gitea, grafana, imgproxy, mattermost, n8n, nextcloud, outline;sonarqubegoes first in #2, which settles whether an embedded Elasticsearch and startup plugin installation are worth reimplementing or better wrapped - Leave
gitlabandsentryon their upstream charts; a dozen coordinated components in one case and Kafka with ClickHouse in the other are beyond what astandardwrapper buys back