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.
Running it
Section titled “Running it”helm template ./chart | rtz risk check -f - --fail-on highInput 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.
Two modes
Section titled “Two modes”Single manifest
Section titled “Single manifest”The default. Every rule that can be evaluated from the manifest alone runs against it.
Diff mode
Section titled “Diff mode”deployment-risk --base base.yaml head.yamlRuns 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.
Output
Section titled “Output”- 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.
Exit codes
Section titled “Exit codes”| Code | Meaning |
|---|---|
0 |
Pass — risk level below the threshold |
1 |
Fail — risk level at or above the threshold |
2 |
IO or parse error |
Threshold
Section titled “Threshold”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.
What it checks
Section titled “What it checks”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 |
What the diff catches
Section titled “What the diff catches”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.
Live checks
Section titled “Live checks”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.
In GitHub Actions
Section titled “In GitHub Actions”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.
