Standard Helm Chart
Generic universal Helm chart easy to configure and extend to be used as standard for most applications and use cases.
Chart version: 0.1.0
Changelog: CHANGELOG.mdSee the values.yaml for all configuration options.
Requirements
Install
Add the helm repository
helm repo add hosst https://hosstio.github.io/helm-chartsInstall the standard Helm chart
helm install example hosst/standardUpgrade
Update the helm chart
helm repo updateCheck the differences
helm diff upgrade example hosst/standardUpgrade the installation
helm upgrade example hosst/standardConfigure
Check values.yaml for all configuration options
Configuration levels
variables, secrets, sealedSecrets, externalSecrets, mountedFiles, mountedSecretFiles and mountedSealedSecretFiles exist at two levels. The release-wide values at the top of values.yaml render <fullname>-variables, <fullname>-secrets, <fullname>-sealed-secrets and <fullname>-external-secrets, and every container with the matching configure flag reads them. A deployment, job or cronJob can set the same keys under its own entry:
deployments:
web:
variables:
LOG_LEVEL: debugThat renders <fullname>-deployment-web-variables (-secrets, -sealed-secrets, -external-secrets, -files, -secret-files, -sealed-secret-files), named after the values key even when the workload sets nameOverride, and read by every container of that workload after the release-wide config, so on a duplicate key the workload's value wins and a file the workload sets replaces the release's file on the same path. The kind is always part of the name: a Deployment named like the release would otherwise produce <fullname>-variables, the release ConfigMap, and a job and a cronJob may share a key.
Config for jobs with a helm.sh/hook follows the hook:
- A
pre-install,pre-upgradeorpre-rollbackjob cannot read the release-wide config, because Helm applies it after those phases, and apre-deletejob finds none when the install failed before applying it. These jobs all read<fullname>-pre-hook-variables(-secrets,-sealed-secrets,-external-secrets,-files,-secret-files,-sealed-secret-files), one shared copy of the release-wide config, created one weight below the first of them in each of their phases. - A
post-deletejob reads<fullname>-post-hook-*, the same kind of copy, because the release-wide config is gone by then. Apost-install,post-upgradeorpost-rollbackjob reads the release-wide config directly. - A hook job's own config holds only its own keys and is a hook resource one weight below the job. The job reads it after the shared copy, so its keys win.
- Hook config is removed when the phase finishes, so a
pre-deletejob's secrets exist only while the delete runs. If a job fails, its config stays until the next run replaces it.
Chart-created persistence volumes are not merged and are not available to pre-install, pre-upgrade or pre-rollback jobs.
Secrets
Three values put secrets into a container's environment, and every container with configure.secrets reads all three through envFrom:
| Value | Renders | The secret value lives in | Needs in the cluster |
|---|---|---|---|
secrets | Secret <fullname>-secrets | the values file and the Helm release history, base64-encoded | nothing |
sealedSecrets | SealedSecret <fullname>-sealed-secrets | the values file, encrypted with kubeseal | the sealed-secrets controller |
externalSecrets | ExternalSecret <fullname>-external-secrets | an external store; the values hold only its key and property | the External Secrets Operator and a SecretStore or ClusterSecretStore |
Use secrets for values that aren't sensitive enough to keep out of Git and the release history, or for local tests. Use sealedSecrets when Git is the only source of truth and the cluster runs no secret store. Use externalSecrets when the team already keeps its secrets in Vault, AWS Secrets Manager, GCP Secret Manager or Azure Key Vault:
externalSecretsStore:
name: vault
kind: ClusterSecretStore # default SecretStore
refreshInterval: 1h # default
externalSecrets:
DATABASE_PASSWORD:
key: myapp/database
property: passwordThe operator creates the Secret <fullname>-external-secrets and deletes it with the ExternalSecret. A change to externalSecrets rolls the deployment's pods; a change to the secret in the store reaches the Secret on the next refresh, but a running pod keeps the old environment until it restarts.
Limitations
This chart only supports Deployment workloads. StatefulSets (databases, message queues, any app needing stable network identity or per-pod storage) and DaemonSets (node agents, log shippers) are not yet supported. StatefulSet support is the top priority on the roadmap.
Troubleshooting
Sealed secrets: pods failing on first install
When you set sealedSecrets or mountedSealedSecretFiles, this chart renders a SealedSecret custom resource, not a Secret. A SealedSecret is just an encrypted blob. The actual Secret your pods consume is created asynchronously by the sealed-secrets controller running in the cluster: it watches for SealedSecret objects, decrypts them, and creates a matching Secret with an owner reference.
Helm applies the SealedSecret during install but does not wait for the controller to materialize the real Secret. This creates a race on first install of a release: the Deployment's pod may be scheduled before the Secret exists.
Symptoms:
- Volume-mounted sealed secrets (
mountedSealedSecretFiles) orsecretKeyRefwithoptional: false: pod stuck inContainerCreating/CreateContainerConfigError. - Env-var references (
envFrom,secretKeyRef): the kubelet keeps retrying, so the pod self-heals once theSecretappears; it just looks like a transient failure. helm install --waitmay time out, because it blocks on pods that are themselves blocked on the missingSecret.
This race is bounded and usually resolves within seconds (and does not recur on helm upgrade, since the Secret already exists). If a pod stays broken:
Check the controller is running and healthy in its namespace (commonly
kube-systemorsealed-secrets).Check for decryption failures: a rotated or mismatched key is a permanent failure, not a race, and the
Secretwill never appear. Inspect status:shkubectl get sealedsecret <release>-sealed-secrets -o jsonpath='{.status}'Confirm the derived
Secretexists:shkubectl get secret <release>-sealed-secrets
Mitigations: use a generous --timeout (or avoid --wait with a tight timeout) on first install; in GitOps, order the SealedSecret ahead of the Deployment (Argo CD sync-waves, Flux dependsOn) so the controller can reconcile first.