On this page
CommonLayer Helm chart standard, version 1
Applies to every chart published by CommonLayer. Reference implementation:
templates/project-template/chart; worked example: the nginx project's chart/
(repository commonlayer-org/nginx; its tree is kept under projects/nginx until that
repository exists).
Rule ids
The repository scan (docs/plan/13-repository-stewardship.md §11) checks charts against
this standard. A chart that cannot follow a rule declares a deviation with a reason in
commonlayer.yaml (charts[].deviations: [{standard: <id>, reason: <text>}]); the scan
then reports a deviation instead of a failure, and the project page shows it.
| Id | Rule |
|---|---|
values | values.yaml has the standard value blocks (image, imagePullSecrets, nameOverride, fullnameOverride, commonLabels, commonAnnotations, serviceAccount, podSecurityContext, containerSecurityContext, resources, livenessProbe, readinessProbe, service, networkPolicy, podDisruptionBudget) |
secure-defaults | the defaults of "Secure defaults" below, including serviceAccount.automountServiceAccountToken: false |
image-digest | images are referenced by digest (image.digest, rendered by commonlayer.image or a chart helper that pins @digest) |
helper | templates/_commonlayer.tpl is identical to the template's |
schema | values.schema.json exists and is a JSON schema |
Packaging
apiVersion: v2.versionis SemVer and independent ofappVersion(bump it on every chart change);appVersionis the upstream version of the default image line.kubeVersionis set.- Distributed as OCI artifacts (
oci://registry.commonlayer.org/charts/<chart>), signed with cosign (keyless), with build provenance attached (publish.yml). - No third-party library chart.
Chart.yamlcarries the annotationorg.commonlayer/chart-standard: v1.
Shared helper file
templates/_commonlayer.tpl is byte-identical in the template and in every
CommonLayer chart; scripts/check-template-drift.sh fails CI otherwise. Change
it only in the template and copy it everywhere (check-template-drift.sh --fix).
It provides:
| Helper | Use |
|---|---|
commonlayer.name, commonlayer.fullname, commonlayer.chart | names (63 characters) |
commonlayer.labels (dict "context" . "component" "server") | the standard labels below plus commonLabels |
commonlayer.selectorLabels (same arguments) | immutable selector labels (name, instance, component) |
commonlayer.annotations (dict "context" . "annotations" .Values.x) | commonAnnotations merged with object-specific ones |
commonlayer.image (dict "image" .Values.image "context" .) | registry/repository@digest when a digest is set (validated), otherwise registry/repository:tag |
commonlayer.imagePullSecrets, commonlayer.serviceAccountName, commonlayer.podSecurityContext, commonlayer.containerSecurityContext, commonlayer.tplValue | the corresponding blocks |
Chart-specific helpers go into another file (for example _helpers.tpl).
Labels
Every object carries app.kubernetes.io/{name,instance,version,component,part-of,managed-by},
helm.sh/chart and commonLabels. Selectors use only name, instance and
component, so that the helm test pod (component test) is never selected by
a Service.
Image values
image:
registry: registry.commonlayer.org
repository: nginx
tag: "1.30.6-r1" # informational when digest is set
digest: "sha256:..." # the released digest; takes precedence
pullPolicy: IfNotPresent
Released charts ship with digest filled in by the release workflow
(scripts/publish-chart.sh), so a default install pulls exactly the promoted
image. In the repository the value is "" until the first release; "" is also
the documented, explicit way for a user to opt out and use the tag. The
platform's chart.images_pinned check requires released charts to reference
promoted digests.
Required values blocks
Every chart has these top-level values, with these names:
image, imagePullSecrets, nameOverride, fullnameOverride, commonLabels,
commonAnnotations, serviceAccount {create, name, annotations, automountServiceAccountToken},
podSecurityContext, containerSecurityContext, resources, livenessProbe,
readinessProbe, startupProbe, service, networkPolicy, podDisruptionBudget,
topologySpreadConstraints, affinity, nodeSelector, tolerations,
priorityClassName, persistence (stateful charts), metrics {enabled, serviceMonitor},
extraEnv, extraVolumes, extraVolumeMounts, podAnnotations, podLabels.
A probe set to null is disabled (Helm merges maps, so {} cannot remove a
default). A chart whose software has no metrics endpoint keeps the metrics
block and fixes enabled to false in its schema.
Secure defaults
| Setting | Default |
|---|---|
podSecurityContext | runAsNonRoot: true, runAsUser/runAsGroup/fsGroup: 10001, seccompProfile: RuntimeDefault |
containerSecurityContext | the same user, allowPrivilegeEscalation: false, readOnlyRootFilesystem: true, privileged: false, capabilities.drop: [ALL], seccompProfile: RuntimeDefault |
| service account | created, token not mounted (automountServiceAccountToken: false) |
resources | requests and limits set |
| writable paths | emptyDir for /tmp (with sizeLimit) and the image's declared volumes |
networkPolicy.enabled: true | default deny: ingress only to the service ports from pods labelled <fullname>-client: "true" and networkPolicy.ingressFrom; egress only DNS and networkPolicy.extraEgress |
enableServiceLinks | false |
Schema and documentation
values.schema.jsonvalidates every value (additionalProperties: falseat the top level and in each block the chart defines; Kubernetes objects such as affinity are accepted as objects).README.mdis generated byhelm-docsfromREADME.md.gotmpland the# --comments invalues.yaml;scripts/validate.shfails when it is stale.
Tests
| Test | Command (in scripts/test-chart.sh unless noted) |
|---|---|
| lint | helm lint --strict with the defaults and every chart/ci/*.yaml |
| render + schema | helm template for the same value sets, validated with kubeconform -strict against Kubernetes 1.31 and 1.37 schemas and CRD schemas (ServiceMonitor, HTTPRoute), schema sources pinned by commit |
| unit tests | helm unittest suites in chart/tests/*_test.yaml |
| policy | trivy config on the rendered manifests; HIGH or CRITICAL findings fail |
| install/upgrade | scripts/test-chart-kind.sh on kind in CI: install the previous release (when published), upgrade to the working tree, helm test |
chart/ci/*.yaml must together enable every optional object of the chart, so
that each template is rendered and validated at least once.
helm test uses the chart's own image (which carries cl-probe), so no extra
test image has to be trusted.