kubernetes · git:20261011.38d4a57 · 2026-10-11 · sha256 040406b6f6b8ced8

kubernetes git:20261011.38d4a57A

Immutable. This exact content never changes and is served at /api/v1/blob/040406b6f6b8ced8.

---
description: Kubernetes and OpenShift workload generation rules — security defaults, resource limits, probes
globs: ["**/*.yaml", "**/*.yml", "**/deployment*.yaml", "**/pod*.yaml"]
alwaysApply: false
---

# Kubernetes & OpenShift Rules

Always generate Deployments with all of the following. Missing any item is a Critical finding.

## Required fields on every container

```yaml
resources:
  requests:
    cpu: "100m"
    memory: "128Mi"
  limits:
    memory: "256Mi"     # Set memory limit. Omit cpu limit — it causes throttling.

securityContext:
  runAsNonRoot: true
  runAsUser: 1000       # Omit on OpenShift — SCC assigns the UID
  allowPrivilegeEscalation: false
  readOnlyRootFilesystem: true
  capabilities:
    drop: ["ALL"]

livenessProbe:
  httpGet: { path: /healthz, port: 8080 }
  initialDelaySeconds: 10
  periodSeconds: 10

readinessProbe:
  httpGet: { path: /ready, port: 8080 }
  initialDelaySeconds: 5
  periodSeconds: 5
```

## Required ServiceAccount settings

```yaml
automountServiceAccountToken: false
```

## Required pod-level securityContext

```yaml
spec:
  securityContext:
    seccompProfile:
      type: RuntimeDefault
```

## Ingress

Never propose ingress-nginx for new work: its best-effort maintenance ended in March 2026 (no releases, bug fixes or security fixes). Default to Gateway API with Envoy Gateway.

- One shared `Gateway` owned by the platform team; app teams own `HTTPRoute`s in their own namespaces
- Bind each HTTPS listener to the namespace that owns its hostname (`allowedRoutes` selector on `kubernetes.io/metadata.name`); never `from: All`, never one shared opt-in label
- Gateway API CRDs on the **standard** channel, applied as plain manifests, never a Helm release (too large for its 1 MiB Secret), and never garbage-collected: Flux `prune: false` + `deletionPolicy: Orphan` (deleting a CRD deletes every Gateway and route). Envoy Gateway's controller chart bundles experimental CRDs: set `crds.enabled: false`
- App namespaces belong to the platform layer, not the app layer
- TLS via cert-manager v1.15+ with `config.gatewayAPI.enabled: true`; one HTTPS listener per hostname with `hostname`, `tls.mode: Terminate`, `certificateRefs`
- Flux: `infrastructure` (CRDs, controllers) → `infrastructure-configs` (Gateway objects) → `apps` (routes), with a `healthCheckExprs` check on Gateway `Programmed`
- Port-80 HTTPS redirect with `statusCode: 308`; `backendRefs` use the Service port **number**
- Migrating: inventory annotations first; treat every ingress2gateway warning as blocking; run both controllers in parallel and move traffic by DNS one hostname at a time; decommission ingress-nginx in a separate change after a soak

## Never generate

- `privileged: true`
- `hostNetwork: true` / `hostPID: true` / `hostIPC: true`
- `image: myapp:latest` — always pin to a digest or explicit tag
- Missing `PodDisruptionBudget` for services with `replicas > 1`
- Missing `NetworkPolicy` for any namespace that handles sensitive data
- ingress-nginx (`ingressClassName: nginx`, the `ingress-nginx` chart) for new work: use an `HTTPRoute` on the shared Gateway