Search across all documentation pages
securityContext, and Pod Security Standards (restricted).The shortest path is to render the manifests, then run a policy check against the rendered output.
helm template myapp ./charts/myapp > /tmp/rendered.yaml
kubeconform -strict -kubernetes-version 1.36.2 /tmp/rendered.yaml
kube-linter lint /tmp/rendered.yamlkubeconform validates schema against the target Kubernetes minor; kube-linter applies best-practice checks.
Render first, always. Linting a Helm chart's raw templates checks Go template text, not the YAML that actually reaches the API server.
Here is a Deployment carrying the defects the skill flags most often.
Before:
apiVersion: apps/v1
kind: Deployment
metadata:
name: checkout
spec:
replicas: 3
selector:
matchLabels:
app: checkout
template:
metadata:
labels:
app: checkout
spec:
containers:
- name: checkout
image: registry.example.com/checkout:latest
ports:
- containerPort: 8080The skill reports six findings: a mutable :latest tag, no probes, no resource requests or limits, no securityContext, no recommended labels, and no PodDisruptionBudget for a 3-replica service.
After:
apiVersion: apps/v1
kind: Deployment
metadata:
name: checkout
labels:
app.kubernetes.io/name: checkout
app.kubernetes.io/version: "1.8.3"
app.kubernetes.io/managed-by: Helm
spec:
replicas: 3
selector:
matchLabels:
app.kubernetes.io/name: checkout
template:
metadata:
labels:
app.kubernetes.io/name: checkout
spec:
securityContext:
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
containers:
- name: checkout
image: registry.example.com/checkout:1.8.3
ports:
- containerPort: 8080
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
memory: 256Mi
startupProbe:
httpGet:
path: /healthz
port: 8080
failureThreshold: 30
periodSeconds: 2
readinessProbe:
httpGet:
path: /readyz
port: 8080
periodSeconds: 5
livenessProbe:
httpGet:
path: /healthz
port: 8080
periodSeconds: 10
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]The image is pinned to the release version, so a rollback is a real rollback rather than a coin flip.
Probes are split by job: startup absorbs slow boot, readiness gates traffic, liveness restarts a wedged process.
The pod- and container-level securityContext fields together satisfy the restricted Pod Security Standard.
Readiness answers "should this pod receive traffic" and controls Endpoints membership.
Liveness answers "is this process wedged" and its only remedy is a container restart.
Startup answers "is boot still in progress" and suspends the other two until it passes.
The skill flags a liveness probe that shares a path with readiness, because a slow dependency then turns a traffic pause into a restart loop.
Requests drive scheduling and are what the scheduler reserves; limits cap runtime consumption.
A pod with equal requests and limits on every resource is Guaranteed; unequal is Burstable; neither is BestEffort and it is evicted first under node pressure.
Memory limits are enforced by the kernel with an OOM kill, so set memory requests equal to limits for predictable behavior.
CPU limits throttle rather than kill, and aggressive CPU limits are a common latency cause - many teams set CPU requests and omit CPU limits deliberately.
The skill requires requests on every container and flags a missing memory limit.
The app.kubernetes.io/* label set is the community convention that dashboards, cost tooling, and NetworkPolicies key off.
spec.selector.matchLabels is immutable after creation, so the skill flags selectors built from volatile values like a version or a commit SHA.
The restricted profile requires runAsNonRoot, allowPrivilegeEscalation: false, capabilities.drop: ["ALL"], and a seccompProfile of RuntimeDefault or Localhost.
Enforcement comes from namespace labels, and the skill checks the manifest would survive them.
apiVersion: v1
kind: Namespace
metadata:
name: payments
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/enforce-version: v1.36Pin enforce-version rather than letting it float to latest, so a cluster upgrade cannot silently tighten admission on your workloads.
A liveness probe with an aggressive failureThreshold during slow startup produces an endless restart loop; use a startup probe instead of loosening liveness.
readOnlyRootFilesystem: true breaks apps that write temp files, so pair it with an emptyDir mounted at /tmp rather than dropping the setting.
Setting only a memory limit with no request makes the pod Burstable with a request equal to the limit - the skill still flags it because the intent is ambiguous.
Warning-mode Pod Security labels are silent in CI; the skill catches restricted violations before the namespace ever rejects them.
A PodDisruptionBudget with minAvailable equal to replicas blocks every node drain, stalling cluster upgrades indefinitely.
kube-linter is a fast standalone checker with a good default rule set and is the right baseline when you want zero configuration.
OPA Gatekeeper and Kyverno enforce the same rules at admission, which is stronger because it also covers manifests applied outside CI.
kubeconform only validates schema, so it complements rather than replaces a best-practice linter.
Use the skill in CI for explained, fixable findings at review time; use an admission controller as the backstop that no one can bypass.
Should this run against Helm charts or rendered YAML?
Rendered YAML, produced by helm template or kustomize build, because that is what the API server actually receives.
Does it replace an admission controller?
No - CI checks give fast feedback with context, admission enforces at the cluster boundary. Run both.
Which Kubernetes version should it validate against?
Pin it to the minor your clusters run, currently 1.36.2, and bump it deliberately as part of the upgrade.
Why flag :latest if we always deploy the same digest?
Because :latest makes the manifest non-reproducible; if you resolve to a digest at deploy time, have the skill assert the digest is present.
Can it enforce probes on Jobs?
Readiness is meaningless for a Job, so the skill scopes probe checks to long-running controllers and skips Jobs and CronJobs.
Stack versions: This page was written for Kubernetes 1.36.2, Docker Engine 29.6.1 (BuildKit default), containerd (CRI runtime on nodes), Helm 3, Compose v2, Argo CD (latest - verify at build), and Gateway API (GA - verify controller support at build).
Reviewed by Chris St. John·Last updated Jul 16, 2026