Skip to content

The deployment risk gate

The deployment risk gate scores a rendered Kubernetes manifest and fails the build when it crosses a threshold. It runs before anything reaches a cluster, so the first time a bad change is caught is at review, not at rollout.

It is stateless and cluster-free by default: give it YAML and a threshold and it runs anywhere CI does.

Terminal window
helm template ./chart | rtz risk check -f - --fail-on high

Input is one or more paths to rendered YAML files or directories — directories are recursed for *.yaml and *.yml, and everything is concatenated and evaluated as a single bundle. With no paths, it reads stdin.

The default. Every rule that can be evaluated from the manifest alone runs against it.

Terminal window
deployment-risk --base base.yaml head.yaml

Runs the full single-manifest analysis on head, plus the diff rules — what changed between base and head, and whether the change itself is the risk. Setting the CHANGED_FILES environment variable (newline-separated) additionally activates the changed-files rule.

Resources are matched on (kind, namespace, name). Quantities are compared semantically, so 10Gi and 10240Mi do not register as a change. Secret values are never emitted — only a hash of the sorted data map, so the gate can tell you a Secret changed without printing it.

The diff deliberately tracks a high-signal field set rather than deep-diffing every key. A full YAML diff is too noisy to gate on.

  • stdout — a Markdown report, formatted for posting as a PR comment.
  • stderr — GitHub Actions workflow commands (::error, ::warning, ::notice), so findings annotate the diff inline.
Code Meaning
0 Pass — risk level below the threshold
1 Fail — risk level at or above the threshold
2 IO or parse error

Default HIGH. Set it with the DEPLOYMENT_RISK_FAIL_ON environment variable; the deployment.risk.failOn system property takes precedence over the env var. A malformed value is an error, not a silent fall-back to the default.

90 rules, of which 37 are diff rules. The static set covers:

Area Examples
Security context privileged escalation allowed or unset, dangerous capability adds, host ports
API lifecycle removed APIs, deprecated APIs
Probes no startup probe, identical liveness and readiness, low thresholds, timeout sanity
HPA min/max inverted, missing scaleTargetRef, target too high, no scaling possible
Jobs & CronJobs no active deadline, no backoff limit, concurrency allowed, no starting deadline
Rollout safety maxUnavailable: 0 with maxSurge: 0, DaemonSet OnDelete
Networking no NetworkPolicy, Ingress without TLS
Images critical and high CVEs, image pull secrets
Resources ephemeral storage with no limit
Governance missing required labels, invalid names, default namespace

Diff rules fire on the change, not the end state — the class of problem a static scan of the head manifest cannot see, because the head looks fine on its own:

  • Capacity reduced — replicas decreased, CPU or memory limit reduced, PVC storage shrunk.
  • Safety removed — liveness, readiness or startup probe removed; PDB removed or weakened; anti-affinity removed; topology spread removed; tolerations removed.
  • Autoscaling weakened — HPA removed, target raised, or otherwise weakened.
  • Exposure widened — Service type exposed, Ingress TLS removed, Ingress protection removed, NetworkPolicy removed.
  • Scheduling narrowed — node selector narrowed or tightened, node affinity tightened.
  • Identity changed — selector changed, Service ClusterIP changed, StatefulSet volume claim changed, Job template changed, immutable data changed. These are the ones that force a replace rather than an update.
  • Context — image changed, config consumed by one or many workloads, GitOps-managed resources, changed files.

Beyond the manifest, the gate can compare what you are proposing against what the cluster is actually doing — a limit cut below observed usage, an HPA that cannot meet observed demand, a grace period shorter than observed shutdown time. These need credentials and are configured per-check.

See Live checks.

The gate ships as a container image, so a workflow step needs no JVM setup. Point it at your rendered manifests, set the threshold, and let the Markdown report become the PR comment.