Helm chart values see history edit this page

Talks about: , , , , and

The stageset-controller Helm chart lives in the metio/helm-charts monorepo and is published at oci://ghcr.io/metio/helm-charts/stageset-controller. The table below is generated from the chart’s values.schema.json, so it tracks the chart’s current schema rather than a hand-maintained copy.

For how the values map onto the binary’s runtime behaviour, see the Configuration reference — every controller.* value drives the corresponding --flag.

ValueTypeDefaultDescription
affinityobject(empty)Affinity rules for scheduling.
autoscalingobject(empty)HorizontalPodAutoscaler (rendered only when replicas.max > replicas.min).
autoscaling.targetCPUUtilizationPercentageinteger80
controllerobject(empty)Controller behaviour (maps onto the binary's flags).
controller.allowedActionHostsarray(empty)Host globs permitted for http actions (SSRF guard). Loopback/link-local are always denied unless explicitly listed here.
controller.defaultIntervalstring10mReconcile cadence for StageSets that omit spec.interval. Most StageSets can leave spec.interval unset and inherit this.
controller.inventoryModestringhybridInventory strategy for tracking applied objects.
controller.inventoryShardCapinteger5000Maximum entries per StageInventory shard.
controller.leaderElectbooleantrueLeader election for HA replicas. Keep on; harmless single-replica.
controller.noCrossNamespaceRefsbooleanfalseDeny cross-namespace sourceRef / dependsOn references.
controller.runbookBaseURLstringhttps://stageset.projects.metio.wtf/runbooksURL prefix appended to actionable Ready messages as (runbook: <base>/<reason>/). Defaults to the documentation site; override to a fork or an internal mirror, or set empty to disable the runbook links.
controller.watchNamespacesarray(empty)Namespaces this controller watches. Empty (the default) means cluster-wide. When set, the manager's cache only observes StageSets and sources in these namespaces, and RBAC pivots from a cluster-wide ClusterRoleBinding to one RoleBinding per listed namespace — the multi-tenant controller-instances pattern (one release per tenant-group, disjoint watch sets).
crdsobject(empty)CRD lifecycle. The chart ships the controller's CRDs under templates/ so a `helm upgrade` applies schema changes automatically. Set create=false to manage them out-of-band — e.g. pre-installed cluster-wide, or in CI where the same chart is installed once per values file and a kept cluster-scoped CRD owned by the first release can't be re-adopted by the next.
crds.createbooleantrue
deploymentobject(empty)Extra labels and revision history for the Deployment.
deployment.additionalLabelsobject(empty)
deployment.revisionHistoryLimitinteger10
gateobject(empty)Flagger stage-gate endpoint (GET /gate/{ns}/{stageset}/{stage}). Renders a dedicated Service when enabled.
gate.enabledbooleantrue
globalobject(empty)Global values are values that can be accessed from any chart or subchart by exactly the same name.
imageobject(empty)Container image. tag defaults to .Chart.AppVersion (the released controller version); override only for a custom build.
image.pullPolicystringIfNotPresentPull policy. IfNotPresent suits tagged release images; switch to Always for a mutable tag (latest, edge).
image.registrystringghcr.io
image.repositorystringmetio/stageset-controller
image.tagstring(empty)Explicit tag override; empty defaults to .Chart.AppVersion.
imagePullSecretsarray(empty)Image pull secrets for a private registry.
metricsobject(empty)Prometheus integration.
metrics.prometheusRuleobject(empty)Opt-in PrometheusRule with a starter alert set on the custom stageset_* metrics plus controller-runtime signals. Requires the Prometheus operator CRDs.
metrics.prometheusRule.annotationsobject(empty)Extra annotations on the PrometheusRule object.
metrics.prometheusRule.enabledbooleanfalse
metrics.prometheusRule.extraAlertLabelsobject(empty)Labels merged onto EVERY rendered alert — route all stageset alerts through one Alertmanager receiver (e.g. team: platform).
metrics.prometheusRule.extraRulesarray(empty)Extra alert rules appended under a separate "stageset-extras" group, rendered verbatim. To silence a built-in alert, raise its threshold below to an impossibly high value rather than forking the chart.
metrics.prometheusRule.intervalstring30sEvaluation interval for every rule group.
metrics.prometheusRule.labelsobject(empty)Extra labels on the PrometheusRule object (typically what your Prometheus selects on).
metrics.prometheusRule.runbookAnnotationKeystringrunbook_urlAnnotation key the runbook URL lands under (runbook_url is the Prometheus-operator convention).
metrics.prometheusRule.runbookBaseURLstringhttps://stageset.projects.metio.wtf/runbooksPrefix for per-alert runbook page links. Defaults to the documentation site; the per-reason alert appends /<reason>/, fixed alerts append /<page>/.
metrics.prometheusRule.thresholdsobject(empty)Numeric/duration thresholds in one place so operators can tune the noise floor without editing rule bodies.
metrics.prometheusRule.thresholds.podDownDurationstring5mStageSetControllerPodDown: NotReady duration before paging. Below 5m it fires during normal pod rolls.
metrics.prometheusRule.thresholds.reconcileErrorDurationstring10m
metrics.prometheusRule.thresholds.reconcileErrorRatenumber0.1StageSetReconcileErrorsHigh: per-StageSet Ready=False rate (per second, 5m window). 0.1/s ≈ one failure every 10s — a stuck StageSet, not a blip.
metrics.prometheusRule.thresholds.reconcileLatencyDurationstring15m
metrics.prometheusRule.thresholds.reconcileLatencySecondsinteger30StageSetReconcileLatencyHigh: reconcile p99 ceiling, seconds. 30s is generous — an artifact fetch plus a multi-stage apply.
metrics.prometheusRule.thresholds.webhookCertRenewalFailuresDurationstring30m
metrics.prometheusRule.thresholds.webhookCertRenewalFailuresPerHourinteger1StageSetWebhookCertRenewalFailing: self-signed cert renewal failures per hour before paging.
metrics.prometheusRule.thresholds.workqueueDepthinteger50StageSetControllerWorkqueueDepthHigh: queued reconcile requests before alerting. 50 absorbs a normal burst, low enough to catch a real stall.
metrics.prometheusRule.thresholds.workqueueDurationstring15m
metrics.serviceMonitorobject(empty)
metrics.serviceMonitor.additionalLabelsobject(empty)
metrics.serviceMonitor.enabledbooleanfalse
metrics.serviceMonitor.intervalstring30s
namespaceobject(empty)Install namespace with Pod-Security labels. The chart installs into .Release.Namespace; set create to render it with PSS labels.
namespace.createbooleanfalse
namespace.pssLevelstringrestricted
networkPolicyobject(empty)Opt-in NetworkPolicy. Inert without a NetworkPolicy controller.
networkPolicy.additionalIngressarray(empty)Extra ingress rules merged verbatim. Applies to the kubernetes engine only.
networkPolicy.calicoobject(empty)Calico engine (engine=calico) raw passthrough. Entries are merged verbatim into the projectcalico.org NetworkPolicy's spec.ingress / spec.egress.
networkPolicy.calico.egressarray(empty)Extra spec.egress rules merged verbatim (Calico NetworkPolicy schema).
networkPolicy.calico.ingressarray(empty)Extra spec.ingress rules merged verbatim (Calico NetworkPolicy schema).
networkPolicy.ciliumobject(empty)Cilium engine (engine=cilium) raw passthrough. Entries are merged verbatim into the CiliumNetworkPolicy's spec.ingress / spec.egress. This is how you tighten the cilium policy — e.g. add fromEndpoints to the ingress rules, or additional toEndpoints / toFQDNs egress.
networkPolicy.cilium.egressarray(empty)Extra spec.egress rules merged verbatim (CiliumNetworkPolicy schema).
networkPolicy.cilium.ingressarray(empty)Extra spec.ingress rules merged verbatim (CiliumNetworkPolicy schema).
networkPolicy.clusterNetworkPolicyobject(empty)ClusterNetworkPolicy engine (engine=clusterNetworkPolicy) raw passthrough. Entries are merged verbatim into the policy.networking.k8s.io ClusterNetworkPolicy's spec.ingress / spec.egress.
networkPolicy.clusterNetworkPolicy.egressarray(empty)Extra spec.egress rules merged verbatim (ClusterNetworkPolicy schema).
networkPolicy.clusterNetworkPolicy.ingressarray(empty)Extra spec.ingress rules merged verbatim (ClusterNetworkPolicy schema).
networkPolicy.clusterNetworkPolicy.priorityinteger1000spec.priority on the ClusterNetworkPolicy (lower wins within the tier).
networkPolicy.defaultDenyobject(empty)Render a namespace-wide default-deny policy so EVERY pod in the namespace is denied by default and the per-workload allowlists are the only exceptions (zero-trust namespace). OFF by default because it also denies co-located workloads — only enable it when the controller has its own namespace. Leaving it off keeps a pod-scoped setup: only this controller's pods are locked down, neighbours untouched.
networkPolicy.defaultDeny.enabledbooleanfalse
networkPolicy.defaultDeny.orderinteger2000Calico/ClusterNetworkPolicy sort key for the deny-all. It must rank the deny-all AFTER the per-workload allowlists so the allowlists win: Calico evaluates lower order first (the allowlist policies carry no order = lowest precedence, so this defaults high), and ClusterNetworkPolicy gives lower priority numbers higher precedence (the allowlist priority defaults to networkPolicy.clusterNetworkPolicy.priority, so this defaults higher). The kubernetes and cilium engines have no precedence knob — deny + allow simply combine additively, allow wins.
networkPolicy.egressobject(empty)
networkPolicy.egress.dnsbooleantrueWhen egress is enabled, allow DNS (UDP+TCP 53) to the cluster DNS namespace.
networkPolicy.egress.dnsNamespacestringkube-systemNamespace of the cluster DNS service (matched by kubernetes.io/metadata.name).
networkPolicy.egress.enabledbooleanfalseRender egress rules. Adds Egress to policyTypes, so everything not explicitly allowed is denied. Off by default: enable only after allowing everything the controller needs (DNS, the kube-apiserver, source-controller / producer namespaces, S3, the OTLP collector, and any remote-cluster or http-action targets) via egress.to — enabling egress without allowing the apiserver cuts the controller off from the cluster.
networkPolicy.egress.toarray(empty)Additional egress peers merged verbatim into spec.egress. This is where you allow the kube-apiserver (an ipBlock CIDR), the source-controller / producer namespaces, and external endpoints (S3, OTLP, remote-cluster or http-action targets). NetworkPolicy cannot select the apiserver by label, so it must be an ipBlock here.
networkPolicy.enabledbooleanfalseRender the NetworkPolicy. When enabled, the policy targets the controller pod; with a NetworkPolicy controller (Calico/Cilium/etc.) installed, anything not listed below is denied. Leave OFF on clusters without such a controller — the policy is silently inert there.
networkPolicy.enginestringkubernetesPolicy engine to render for. kubernetes (default) emits a vanilla networking.k8s.io NetworkPolicy. cilium / calico / clusterNetworkPolicy emit the engine-native equivalent instead (CiliumNetworkPolicy, projectcalico.org NetworkPolicy, or policy.networking.k8s.io ClusterNetworkPolicy). The per-port .from knobs below apply to the kubernetes engine only; alternative engines are pod-scoped allow-all on the required ports and tighten via their native passthrough lists (cilium/calico/clusterNetworkPolicy .ingress / .egress).
networkPolicy.gateobject(empty)Sources allowed to reach the gate port. Empty = all (e.g. Flagger for the gate).
networkPolicy.gate.fromarray(empty)
networkPolicy.metricsobject(empty)Sources allowed to reach the metrics port. Empty = all (e.g. Prometheus for metrics).
networkPolicy.metrics.fromarray(empty)
networkPolicy.webhookobject(empty)Sources allowed to reach the webhook port. Empty = all (e.g. kube-apiserver for the webhook, which cannot be a podSelector). Applies to the kubernetes engine only.
networkPolicy.webhook.fromarray(empty)
nodeSelectorobject(empty)Node selector for scheduling.
podobject(empty)Extra labels for the pod template.
pod.additionalLabelsobject(empty)
podDisruptionBudgetobject(empty)PodDisruptionBudget (rendered only when replicas.max > replicas.min).
podDisruptionBudget.minAvailableinteger1
podSecurityContextobject(empty)Pod-level securityContext (PSS restricted).
podSecurityContext.runAsNonRootbooleantrue
podSecurityContext.seccompProfileobject(empty)
podSecurityContext.seccompProfile.typestringRuntimeDefault
portsobject(empty)Ports the controller binds.
ports.gateinteger8082read-only Flagger stage-gate endpoint
ports.healthinteger8081/healthz + /readyz probes
ports.metricsinteger8080controller-runtime Prometheus metrics
ports.webhookinteger9443validating admission webhook
rbacobject(empty)Controller RBAC. The default grant is deliberately minimal: the controller impersonates each StageSet's spec.serviceAccountName for cluster writes, so a tenant's own RBAC bounds what its StageSets can touch.
rbac.clusterAdminbooleanfalseBind the controller ServiceAccount to the built-in cluster-admin ClusterRole. Single-tenant clusters where StageSets omit spec.serviceAccountName run the controller under its own identity; cluster-admin lets it apply any kind cluster-wide — the model Flux's helm-controller uses in its default install. Leave false for multi-tenant clusters, where every StageSet sets serviceAccountName and the controller only impersonates those scoped SAs.
replicasobject(empty)Replica bounds. min is the Deployment replica count; when max > min an HPA and a PodDisruptionBudget are rendered. At the default 1/1 both are omitted.
replicas.maxinteger1
replicas.mininteger1
resourcesobject(empty)Pod resource sizing. Applied to both requests and limits (cpu, memory, and ephemeral-storage) so the pod is fully constrained — kube-score's resource-completeness checks require all three on both sides.
resources.cpustring50m
resources.ephemeralStoragestring32Mi
resources.memorystring256Mi
rollbackStoreobject(empty)Optional rollback store for bit-exact, GC-independent rollbacks.
rollbackStore.backendstringnonenone: rollback falls back to re-fetching the producer artifact. pvc: an RWX PersistentVolumeClaim (use RWX for HA replicas). s3: any S3-compatible bucket.
rollbackStore.pvcobject(empty)
rollbackStore.pvc.accessModesarray(empty)
rollbackStore.pvc.mountPathstring/var/lib/stageset/rollback
rollbackStore.pvc.sizestring1Gi
rollbackStore.pvc.storageClassstring(empty)
rollbackStore.s3object(empty)
rollbackStore.s3.anonymousbooleanfalse
rollbackStore.s3.bucketstring(empty)
rollbackStore.s3.endpointstring(empty)
rollbackStore.s3.existingSecretstring(empty)Secret holding accessKey / secretKey / sessionToken keys. Empty static creds engage minio-go's IAM/IRSA discovery chain.
rollbackStore.s3.prefixstring(empty)
rollbackStore.s3.regionstring(empty)
rollbackStore.s3.ssestrings3Server-side encryption at rest for stored objects. The rollback store holds rendered Secret data, so this defaults on. s3 = SSE-S3 (bucket-managed key); kms = SSE-KMS with sseKmsKeyId; none only for a backend that cannot honor an SSE header. A rejected SSE write is non-fatal — it warns and skips the store write, the rollout still succeeds.
rollbackStore.s3.sseKmsKeyIdstring(empty)KMS key ARN/ID for sse: kms; empty uses the bucket's default KMS key.
rollbackStore.s3.useSSLbooleantrue
securityContextobject(empty)Container-level securityContext (PSS restricted). runAsUser/runAsGroup are set above 10000 (kube-score requirement) for a static binary that needs no real user identity.
securityContext.allowPrivilegeEscalationbooleanfalse
securityContext.capabilitiesobject(empty)
securityContext.capabilities.droparray(empty)
securityContext.readOnlyRootFilesystembooleantrue
securityContext.runAsGroupinteger65532
securityContext.runAsNonRootbooleantrue
securityContext.runAsUserinteger65532
securityContext.seccompProfileobject(empty)
securityContext.seccompProfile.typestringRuntimeDefault
serviceAccountobject(empty)Controller ServiceAccount.
serviceAccount.annotationsobject(empty)Extra annotations on the ServiceAccount — e.g. eks.amazonaws.com/role-arn for IRSA, so the controller's S3 rollback store assumes an IAM role instead of using static keys.
serviceMeshobject(empty)Opt-in service-mesh L7 authorization + mTLS for the controller pod. Complementary to networkPolicy, NOT a replacement: networkPolicy is L3/L4 (which pods/IPs may open a connection), serviceMesh is L7/identity (which mesh identities may call which port). Both can be enabled at once and stack additively. Authorizes only the mesh-reachable ports — gate (Flagger) and metrics (Prometheus). The webhook (kube-apiserver) and health/probe (kubelet) ports are deliberately left out: those callers speak plain TLS / no mesh identity, and locking them to mesh principals would break admission and health probing.
serviceMesh.defaultDenyobject(empty)Render a namespace-wide default-deny so every pod in the install namespace rejects unauthorized mesh traffic and the per-workload allows above are the only exceptions (zero-trust namespace). Istio renders an empty-spec AuthorizationPolicy (deny-all) scoped to the whole namespace, which sits at lower precedence than the workload ALLOW. Linkerd has no per-object deny-all; the namespace default is set via the config.linkerd.io/default-inbound-policy annotation, so this is stamped onto the chart-managed Namespace (requires namespace.create=true) — otherwise annotate the namespace out-of-band. Enable only when the controller owns its namespace; it also denies co-located workloads.
serviceMesh.defaultDeny.enabledbooleanfalse
serviceMesh.enabledbooleanfalseOpt-in. When enabled, the chart renders the selected engine's authorization + mTLS objects for the controller pod. Inert unless that mesh (Istio or Linkerd) is actually installed and the pod is injected.
serviceMesh.enginestringistioMesh engine to render for. istio emits security.istio.io AuthorizationPolicy (+ optional PeerAuthentication). linkerd emits policy.linkerd.io Server / AuthorizationPolicy / MeshTLSAuthentication objects.
serviceMesh.gateobject(empty)Per-port allowed mesh identities. Each `from` entry is a source matcher: - principals: SPIFFE/mesh identities (Istio source.principals, e.g. cluster.local/ns/<ns>/sa/<sa>). For Linkerd these map to MeshTLSAuthentication identities (the proxy identity string, e.g. <sa>.<ns>.serviceaccount.identity.linkerd.cluster.local, or "*"). - namespaces: source namespaces (Istio source.namespaces). ISTIO-ONLY — Linkerd authenticates by workload identity, not by namespace, so this field is ignored on the linkerd engine. An EMPTY `from` list on a port means OPEN (allow any caller), mirroring networkPolicy's empty-`from`=open semantics. On Istio that is a port rule with no `from`; on Linkerd it is a MeshTLSAuthentication of identities: ["*"] (any authenticated meshed client). Mesh identities allowed to poll the Flagger stage-gate endpoint (ports.gate). Rendered only when gate.enabled. Empty = open.
serviceMesh.gate.fromarray(empty)
serviceMesh.istioobject(empty)Istio engine raw passthrough. Entries are merged verbatim into the AuthorizationPolicy's spec.rules (security.istio.io/v1 rule schema). Use this to add rules the per-port `from` knobs above can't express — path/method matchers, `when` JWT-claim conditions, ipBlocks, etc.
serviceMesh.istio.rulesarray(empty)
serviceMesh.linkerdobject(empty)Linkerd engine raw passthrough. Entries are appended verbatim as additional documents after the rendered Server / AuthorizationPolicy / MeshTLSAuthentication set — each entry must be a complete object (policy.linkerd.io AuthorizationPolicy, HTTPRoute, etc.).
serviceMesh.linkerd.authorizationsarray(empty)
serviceMesh.metricsobject(empty)Mesh identities allowed to scrape the controller metrics port (ports.metrics). Empty = open.
serviceMesh.metrics.fromarray(empty)
serviceMesh.mtlsstring(empty)mTLS posture (Istio engine only). Empty defers to the mesh's own default (mesh-wide PeerAuthentication / MeshConfig). permissive renders a PeerAuthentication accepting both mTLS and plaintext. strict requires mTLS on the workload's ports EXCEPT the webhook + health ports, which get a port-level PERMISSIVE carve-out so the non-mesh kube-apiserver and kubelet still connect. Linkerd negotiates mTLS automatically between meshed pods, so this knob does not apply to the linkerd engine.
tolerationsarray(empty)Tolerations for scheduling.
topologySpreadConstraintsarray(empty)Topology spread constraints for scheduling.
webhookobject(empty)Validating admission webhook for StageSet.
webhook.certManagerobject(empty)cert-manager mode: the issuer that signs the webhook Certificate.
webhook.certManager.issuerRefobject(empty)
webhook.certManager.issuerRef.kindstringIssuer
webhook.certManager.issuerRef.namestring(empty)
webhook.certModestringself-signedTLS provisioning. self-signed (default) has the controller generate a CA + serving cert in-pod and patch the ValidatingWebhookConfiguration caBundle (HA-safe) — no cluster prerequisite, works out of the box. cert-manager renders a Certificate (cert mounted from the issued Secret) and needs cert-manager + a configured issuer.
webhook.certValiditystring8760hValidity of the self-signed serving cert; the renewer rotates at validity/3.
webhook.enabledbooleantrue