# Review Kubernetes Manifests in Pull Requests

Yes. An agent renders your Helm and Kustomize output, runs kubeconform and kube-linter, and comments on containers without limits, probes or a non-root user.

[Run this on Rush](https://cloud.getrush.ai/run/review-kubernetes-manifests/kubernetes/), in a Project that has your repository.

## What the agent does

The agent checks out the pull request's branch in its own sandbox and works out which forms of the manifests the change touches. In a repository like GoogleCloudPlatform/microservices-demo the same eleven-service app ships three ways: raw YAML in `kubernetes-manifests/` (one file per service plus a `kustomization.yaml`), a Helm chart in `helm-chart/` at version 0.10.7, and Kustomize in `kustomize/`, where `base/` holds the services and `components/` holds variants such as `network-policies`, `memorystore`, `spanner`, `service-mesh-istio` and `without-loadgenerator`. A change to one form often needs the same change in the others, and a missing twin is a finding in itself.

It reviews rendered output, not templates, because that is what reaches the cluster. It runs `helm lint --strict` and `helm template .` in `helm-chart/`, plus the variants the repository's `helm-chart-ci.yaml` workflow renders (gRPC health probes, Spanner, the service mesh). It runs `kubectl kustomize .` in `kustomize/` and `kustomize build` for each directory in `kustomize/tests/`, the combinations `kustomize-build-ci.yaml` already checks. Then it runs the same rendering on the base commit and diffs the two, so a comment can say exactly which Deployment field changed and in which variant.

On each rendered file it runs `kubeconform -strict -summary` against the schemas for your cluster's Kubernetes version, which catches a misspelled field or an `apiVersion` the cluster no longer serves, and `kube-linter lint`, whose checks cover missing CPU and memory requests and limits, missing readiness and liveness probes, containers that can run as root, privileged containers, a writable root filesystem and images without a pinned tag. It keeps only results on objects the pull request changed, so the review is about this change and not every old warning in the repository.

Then it reads each finding against the repository's own baseline. `kubernetes-manifests/frontend.yaml` sets a pod `securityContext` with `runAsNonRoot: true`, and a container context with `allowPrivilegeEscalation: false`, `capabilities.drop: [ALL]`, `privileged: false` and `readOnlyRootFilesystem: true`, plus HTTP readiness and liveness probes on `/_healthz` port 8080 with a session cookie header. A new service that omits any of those is a comment pointing at the frontend as the pattern. It notes the cases where a rule does not fit, such as the `frontend-check` init container in `loadgenerator.yaml`, which uses `busybox` pinned by digest and cannot take probes because init containers do not run them.

## Start it with one API call

`POST https://api.prix.dev/o/{org}/sessions`

```json
{
  "agent": "claude",
  "input": "Review the open pull request's Kubernetes changes. Render the Helm chart with helm lint --strict and helm template, and the Kustomize base and each kustomize/tests combination, on this branch and on the base commit, and diff the output. Run kubeconform -strict and kube-linter on the rendered manifests. Comment on the changed lines with each container that lacks resource requests and limits, readiness or liveness probes, or a non-root security context, each privileged or root-capable container, and any change made in one of raw manifests, Helm or Kustomize but not the others. Do not edit the files.",
  "resources": [
    {
      "url": "https://github.com/your-org/your-k8s-app/pull/123"
    }
  ]
}
```

1. **Get an API key.** In the console, open Settings, then API keys, and create a key for your org. Send it in the Authorization header as a Bearer token.
2. **Send the request.** POST the body in request.json to https://api.prix.dev/o/{org}/sessions, with your repository as the resources URL. The run starts in its own cloud sandbox.
3. **Read what came back.** Follow the run on its live stream or wait for the run.completed webhook, then read the result and its trajectory.

## What comes back

What comes back is a review on the pull request with comments on the changed lines in `kubernetes-manifests/`, `helm-chart/templates/` or `kustomize/`. Each comment names the Deployment and container, the rule it breaks (no memory limit, no readiness probe, `runAsNonRoot` missing, `privileged: true`), the tool that reported it, and the YAML to add, written to match the repository's existing services. When a change appears in one form of the manifests and not another, the comment names the file that needs the same change.

The session's transcript holds the full output of `helm template`, `kustomize build`, `kubeconform` and `kube-linter` for both commits, and the rendered diff, so a reviewer can open the exact manifest behind any comment.

Before merging, a reviewer checks that the suggested requests and limits fit the service's real usage, which the sandbox cannot measure, and that a probe path exists in the service's code. A container that can run as root or with `privileged: true` is the comment to settle first.

## Run it on every pull request that changes manifests

An automation with a GitHub trigger on `pull_request.opened` and `pull_request.synchronize` runs this review on every push. To limit it to deploy changes, receive GitHub's `pull_request` webhook on your server, check whether the changed files are under `kubernetes-manifests/`, `helm-chart/` or `kustomize/`, and send this body with the pull request URL in `resources` and the head SHA as the `Idempotency-Key`.

Set a webhook URL on the API key and Rush POSTs `run.completed` when the review is posted. If the author asks a question in the review thread, forward it as a follow-up message to the same session; the parked sandbox still has both renders and the tool output.

## Why run it on Rush?

Reviewing manifests from a diff misses what Helm values and Kustomize patches produce, and running `helm template`, `kustomize build`, `kubeconform` and `kube-linter` on two commits for every pull request is a setup most reviewers do not keep on their laptop. On Rush each review runs in its own sandbox with those tools installed for that run and no kube context, so rendering a chart cannot apply anything to a cluster.

Each comment is backed by tool output in the session's transcript, which makes the review a platform team can rely on for every deploy change rather than a spot check. Reviews for many pull requests run at the same time, each with its own renders, and the result is the same whether the change came from a person or a dependency bot.

## Frequently asked questions

### Does it need access to my cluster?

No. Rendering, `kubeconform` and `kube-linter` all work on files, and the agent gets no kubeconfig. Tell it your cluster's Kubernetes version in the prompt so `kubeconform` validates against the right schemas.

### Will it complain about every old warning in the repository?

No. It renders the base commit as well and keeps only findings on objects the pull request changed. It may mention in the summary that an unchanged service has the same problem, without commenting on lines the pull request did not touch.

### We use Kustomize components and overlays. Does it render all of them?

It renders the overlays the repository already builds in CI, such as each directory in `kustomize/tests/`, plus the base. Name any other overlay you deploy in the prompt and it renders that too.

### What does it suggest for resource limits?

It copies the shape the repository already uses for similar services and says the numbers are a starting point. It cannot measure the service's real CPU and memory from the sandbox, so the reviewer sets the final values.
