Rules as code: OPA, Kyverno and built-in policies
No more "please don't use the latest tag" in the wiki. Enforce it automatically.
No more "please don't use the latest tag" in the wiki. Enforce it automatically.

Every organisation has rules. Containers must not run as root. Every deployment needs an owner label. Images must come from our registry. Production needs at least two replicas. Usually these rules live in a wiki page, and they're followed exactly as well as wiki pages are read.
Policy as code turns those rules into something a computer enforces, on every change, without anyone having to remember them.
Kubernetes has admission control: every create or update request can be checked (validated) or modified (mutated) before it's stored. Policy engines plug in there.
Kyverno, a CNCF project, lets you write policies as Kubernetes resources. If you can read a Deployment, you can read a Kyverno policy:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: disallow-latest-tag
spec:
validationFailureAction: Enforce
rules:
- name: require-image-tag
match:
any:
- resources:
kinds: ["Pod"]
validate:
message: "Images must use a specific version tag, not 'latest'."
pattern:
spec:
containers:
- image: "!*:latest"
Kyverno can also mutate (add default labels or security settings), generate resources (create a default NetworkPolicy in every new namespace), and verify image signatures.
Open Policy Agent (OPA), also a CNCF project, is a general-purpose policy engine using a language called Rego. Gatekeeper integrates it with Kubernetes. Rego is very powerful and works far beyond Kubernetes (APIs, Terraform plans, CI pipelines), but it has a learning curve.
package k8srequiredlabels
violation[{"msg": msg}] {
required := {"owner", "app"}
provided := {label | input.review.object.metadata.labels[label]}
missing := required - provided
count(missing) > 0
msg := sprintf("Missing required labels: %v", [missing])
}
Kubernetes now has its own policy mechanism, ValidatingAdmissionPolicy, stable since version 1.30. Rules are written in CEL (Common Expression Language) and run inside the API server, with no extra component to install:
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
name: require-replicas-in-prod
spec:
matchConstraints:
resourceRules:
- apiGroups: ["apps"]
apiVersions: ["v1"]
operations: ["CREATE", "UPDATE"]
resources: ["deployments"]
validations:
- expression: "object.spec.replicas >= 2"
message: "Production deployments need at least 2 replicas."
(A ValidatingAdmissionPolicyBinding then applies it to specific namespaces.)
It's perfect for simple validation rules. For mutation, generation, image verification and reporting, Kyverno or Gatekeeper still offer more. Both engines are also adopting CEL themselves.
A rule in a wiki is a suggestion. A rule in an admission policy is a guarantee.
A good starter set for most clusters:
latest image tags.owner, app, maybe cost-center.Many of these are available ready-made in the Kyverno policy library and the Gatekeeper library.
The fastest way to make enemies is to switch on twenty policies in enforce mode on a Friday. Instead:
Audit, Gatekeeper's dryrun or warn, or Warn/Audit actions for ValidatingAdmissionPolicy. Violations are reported, nothing is blocked.kyverno test) to run policies against sample resources.opa test for Rego unit tests.For most teams starting out, Kyverno is the easiest: YAML policies, a big library, mutation and image verification included. Use ValidatingAdmissionPolicy for simple rules where you don't want another component. Choose OPA when you want one policy language across Kubernetes, Terraform, APIs and more.
My take: write the rules down once, in code, and let the cluster remember them for you.
— Desly