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
cleanupOnDeleteobject(empty)Pre-delete cleanup hook. StageSets carry a finalizer (stages.metio.wtf/finalizer) that tears down the objects a StageSet applied. A plain `helm uninstall` removes the controller while StageSets still hold that finalizer, so their delete never completes and uninstall hangs. When enabled, a pre-delete Job bulk-deletes every StageSet so the controller's finalizer runs (tearing down applied objects) BEFORE Helm removes the controller itself. Disable when uninstall is driven by something other than Helm (e.g., ArgoCD with hooks off) — the manual cleanup pattern is: kubectl delete stageset --all -A --wait=true --timeout=2m helm uninstall stageset-controller -n <ns>
cleanupOnDelete.activeDeadlineSecondsinteger600Job-level safety net. activeDeadlineSeconds caps the Job's total runtime; if the controller is wedged, Helm won't be blocked forever.
cleanupOnDelete.backoffLimitinteger2backoffLimit on the pre-delete Job.
cleanupOnDelete.enabledbooleantrue
cleanupOnDelete.imageobject(empty)Image for the pre-delete cleanup Job (a kubectl image).
cleanupOnDelete.image.pullPolicystringIfNotPresent
cleanupOnDelete.image.registrystringregistry.k8s.io
cleanupOnDelete.image.repositorystringkubectl
cleanupOnDelete.image.tagstringv1.34.0renovate: datasource=docker depName=registry.k8s.io/kubectl
cleanupOnDelete.kubectlTimeoutstring2mMaximum wall-clock time the pre-delete Job's `kubectl delete stagesets all --wait` waits for the controller's finalizers to tear the StageSets down. On timeout the delete (and the Job) FAILS, blocking the uninstall so finalizers are never silently skipped — the Job does not force-strip them; recover by stripping finalizers by hand (see the job's comments) and re-running uninstall. Bigger when StageSets tear down many objects or talk to slow remote clusters.
cleanupOnDelete.resourcesobject(empty)Resource requests/limits for the cleanup Job container.
cleanupOnDelete.resources.cpustring100m
cleanupOnDelete.resources.ephemeralStoragestring16MiEphemeral-storage requests/limits. The cleanup Job runs a few `kubectl` calls then exits — disk usage is negligible. Sized small to satisfy kube-score's resource-completeness check without crowding cluster ephemeral-storage budgets.
cleanupOnDelete.resources.memorystring64Mi
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.inventoryShardCapinteger5000Maximum entries per StageInventory shard.
controller.leaderElectbooleantrueLeader election for HA replicas. Keep on; harmless single-replica.
controller.logFormatstringjsonLog output format for the controller binary (json, text).
controller.logLevelstringinfoLog verbosity for the controller binary (debug, info, warn, error).
controller.noCrossNamespaceRefsbooleanfalseDeny cross-namespace sourceRef / dependsOn references.
controller.objectLevelKMSbooleanfalseDecrypt SOPS cloud KMS keys under each StageSet's serviceAccountName federated to a cloud identity, instead of the controller's ambient credentials. Opt-in per-tenant KMS identity: the tenant ServiceAccount must be federated (IRSA / Workload Identity) to a cloud identity granted KMS decrypt. Off by default (ambient credentials).
controller.requirePinnedMigrationSourcesbooleanfalseRequire a spec.migrationsSourceRef source to be pinned to an immutable revision (OCIRepository spec.ref.digest / GitRepository spec.ref.commit) before its destructive migration ladder runs, so a tag/branch overwrite can't auto-roll new destructive content. When off, a mutable-pinned source still runs but emits a Warning event. Recommended in production.
controller.requireVerifiedMigrationSourcesbooleanfalseRequire a spec.migrationsSourceRef source to be signature-verified (status.conditions[SourceVerified]=True from the source's spec.verify cosign/notation config) before its destructive migration ladder runs. A source whose verification FAILED is always refused; this additionally refuses sources that configure no verification at all. Recommended in production.
controller.tracingobject(empty)OpenTelemetry tracing for the controller. When endpoint is empty (the default), no spans are emitted and the OTel SDK is in no-op mode. Set the endpoint to ship spans to an OTLP gRPC collector: tracing: endpoint: otel-collector.observability.svc:4317 insecure: true
controller.tracing.endpointstring(empty)OTLP gRPC collector host:port. Empty disables tracing entirely.
controller.tracing.insecurebooleanfalseSkip TLS when dialing the OTLP collector. Use only for in-cluster collectors that don't terminate TLS themselves.
controller.tracing.sampleRationumber1TraceID-ratio sampling (0.0..1.0). 1.0 samples every trace.
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.
mcpobject(empty)MCP (Model Context Protocol) server. Exposes the controller's read-only introspection tools over a streamable-HTTP endpoint. Renders the server flag, container port, and a dedicated Service when enabled.
mcp.allowMutationsbooleanfalseAdditionally register the gated write tools. Off keeps the server read-only.
mcp.enabledbooleanfalseRender the MCP server flag, container port, and Service.
mcp.serviceobject(empty)Dedicated MCP Service. Rendered with the rest of the MCP wiring when mcp.enabled.
mcp.service.enabledbooleantrue
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.thresholdsobject(empty)Numeric/duration thresholds in one place so operators can tune the noise floor without editing rule bodies.
metrics.prometheusRule.thresholds.budgetFrozenDurationstring15mStageSetBudgetFrozen / StageSetStageBudgetFrozen: how long an error-budget freeze (rollout-wide or per-stage) must hold before alerting. Deploys held out of SLO budget are intentional, so this is a heads-up, not an instant page.
metrics.prometheusRule.thresholds.metricSourceErrorsDurationstring15mHow long the per-hour metric-source error increase must stay above the threshold before the alert fires.
metrics.prometheusRule.thresholds.metricSourceErrorsPerHourinteger3StageSetMetricSourceUnavailable: hourly increase on the metric-source error counter before alerting. A fail-open freeze runs with no protection while its source is broken, so this is the loud signal for that hole.
metrics.prometheusRule.thresholds.podDownDurationstring5mStageSetControllerPodDown: NotReady duration before paging. Below 5m it fires during normal pod rolls.
metrics.prometheusRule.thresholds.promotionBlockedDurationstring10mStageSetPromotionBlocked: how long a promotion analysis must stay failed (breached past failureLimit) before alerting.
metrics.prometheusRule.thresholds.promotionPendingDurationstring1hStageSetPromotionStuck: how long a rollout may sit parked at a stage (soak/analysis/manual hold) before alerting. Long by default so a normal short soak never fires — it targets a forgotten manual gate or a misconfigured soak. Raise it where long deliberate holds are expected.
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.watchEngagementFailuresDurationstring30mHow long the per-hour watch-engagement failure increase must stay above the threshold before the alert fires.
metrics.prometheusRule.thresholds.watchEngagementFailuresPerHourinteger1StageSetWatchEngagementFailing: hourly increase on the producer-watch engagement failure counter. The retry pipeline eventually re-engages, but a sustained inability to engage means dependent StageSets aren't re-triggering on upstream producer republishes.
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.mcpobject(empty)Sources allowed to reach the MCP server port. Empty = all. Only rendered when mcp.enabled.
networkPolicy.mcp.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 for any multi-replica install — replicas.min above 1, or replicas.max above replicas.min).
podDisruptionBudget.maxUnavailableinteger|string|nullnullPDB maxUnavailable. Null (default) renders minAvailable instead; set this (and minAvailable to null) to cap voluntary disruptions to N pods at a time.
podDisruptionBudget.minAvailableinteger|string|null1PDB minAvailable (the default shape). Mutually exclusive with maxUnavailable — set this to null when using maxUnavailable.
podDisruptionBudget.unhealthyPodEvictionPolicystring(empty)spec.unhealthyPodEvictionPolicy on the PDB. Empty defers to the cluster default.
podSecurityContextobject(empty)Pod-level securityContext (PSS restricted). fsGroup matches the container's runAsGroup so the kubelet/CSI driver chowns mounted volumes (notably the rollbackStore.backend=pvc volume) to the running group — without it the non-root container (uid/gid 65532) cannot write to a freshly provisioned, root-owned PVC and the controller crashloops at startup. fsGroupChangePolicy OnRootMismatch skips the recursive chown when the volume root already matches, avoiding slow restarts on large volumes.
podSecurityContext.fsGroupinteger65532
podSecurityContext.fsGroupChangePolicystringOnRootMismatch
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.mcpinteger8084MCP (Model Context Protocol) server (mcp.enabled only)
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.
rbac.ledgerViewbooleanfalseAggregate read (get/list/watch) on StageLedgers into the built-in `view` ClusterRole, so a namespace user who can already view their StageSets can see why a scope: Lifetime action was skipped (which bootstrap already ran). Off by default: it widens what `view` exposes, a deliberate choice on a multi-tenant permission surface. Enabling it grants read only — never write; forgetting a completion stays an operator action via `stagesetctl reset-ledger`.
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. ephemeralStorage must exceed tmpSizeLimit (the /tmp emptyDir, which holds the streamed artifact capped at the binary's 64Mi download limit plus the decrypted build tree): sizing it below would let the kubelet evict the pod mid-fetch on any artifact larger than the limit.
resources.cpustring50m
resources.ephemeralStoragestring320Mi
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)Existing Secret carrying AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, and optionally AWS_SESSION_TOKEN. It is wired into the pod via envFrom.secretRef; the controller's AWS credential chain picks the keys up automatically. Leave empty to engage the IAM/IRSA discovery chain (AWS_* env vars, EKS web-identity, EC2 metadata).
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.mcpobject(empty)Mesh identities allowed to reach the MCP endpoint (ports.mcp). Rendered only when mcp.enabled. Empty = open.
serviceMesh.mcp.fromarray(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.
tmpSizeLimitstring256MiSize limit for the writable /tmp emptyDir (artifact download + decrypted kustomize build scratch). Kept below resources.ephemeralStorage so an over-large artifact trips this with a clear message before kubelet eviction.
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
webhook.failurePolicystringFailFailure policy for the ValidatingWebhookConfiguration. Fail is the safer default — a webhook outage blocks StageSet writes cluster-wide until the controller is back. Switch to Ignore only when you're willing to accept invalid StageSets reaching the reconciler. During a controller restart (rolling update, crash-loop, eviction) the webhook is briefly unreachable and Fail blocks every StageSet create/update for that window. If your CI/GitOps tooling can't tolerate that, scope the webhook with objectSelector / namespaceSelector to limit the blast radius, or set failurePolicy: Ignore.
webhook.matchConditionsarray(empty)matchConditions are CEL expressions evaluated server-side before the webhook is dialed. Lets operators short-circuit admission checks for specific StageSet shapes without a round trip. Requires Kubernetes 1.30+.
webhook.namespaceSelectorobject(empty)namespaceSelector restricts the webhook to Namespaces carrying the listed labels. A common pattern: opt-in per namespace via a label so the apiserver doesn't dial the webhook for unrelated namespaces during controller outages. Example: namespaceSelector: matchLabels: stages.metio.wtf/admission: enforce
webhook.objectSelectorobject(empty)objectSelector restricts the webhook to StageSets carrying the listed labels. Useful when only some StageSets are controller-managed (the rest live in Argo or kustomize). Example: objectSelector: matchLabels: stages.metio.wtf/managed: "true"
webhook.sideEffectsstringNoneSide-effect mode for the ValidatingWebhookConfiguration.
webhook.timeoutSecondsinteger10Webhook call timeout in seconds. The apiserver aborts the admission call after this and applies failurePolicy. Keep low so a wedged controller fails fast instead of stalling every StageSet apply.