Kyverno 1.14 (released April 25, 2025) introduces dedicated ValidatingPolicy and ImageValidatingPolicy types that leverage CEL (Common Expression Language) - the same validation language Kubernetes now uses natively, creating a unified policy experience across your entire stack.
For platform engineers and security teams implementing policy-as-code in Kubernetes, this convergence represents a fundamental shift toward standardization and performance. Let’s explore why this matters and how to get started.
The CEL Convergence
Kubernetes’ adoption of CEL for native validation creates an unprecedented opportunity for policy engines like Kyverno to standardize on the same language. This convergence delivers three critical benefits:
One expression language to learn across Kubernetes and Kyverno means your team invests in skills that transfer between CRD validation rules, ValidatingAdmissionPolicies, and now Kyverno policies.
Better performance - CEL expressions are parsed once and compiled to bytecode, providing significant speed improvements over traditional validation methods.
Reusable validation logic between native Kubernetes and Kyverno policies eliminates the need to maintain separate validation implementations.
Kubernetes now uses CEL in multiple contexts:
- CRD validation rules (beta in 1.25, GA in 1.29)
- ValidatingAdmissionPolicies (beta in 1.28, GA in 1.30)
- Future features like ResourceQuota expressions and RBAC conditions
This means mastering CEL for Kyverno directly translates to expertise across the entire Kubernetes ecosystem.
Your First ValidatingPolicy
Let’s start with a practical example that demonstrates the power and simplicity of Kyverno’s new ValidatingPolicy with CEL:
apiVersion: policies.kyverno.io/v1alpha1kind: ValidatingPolicymetadata: name: require-pod-resourcesspec: matchConstraints: resourceRules: - apiGroups: [""] apiVersions: [v1] operations: [CREATE, UPDATE] resources: [pods] validations: - expression: | object.spec.containers.all(container, has(container.resources.limits) && has(container.resources.limits.memory) && has(container.resources.limits.cpu) ) message: "All containers must have CPU and memory limits defined" - expression: | object.spec.containers.all(container, container.resources.limits.memory.matches('^[0-9]+(Mi|Gi)$') && size(container.resources.limits.memory) <= size('8Gi') ) message: "Memory limits must not exceed 8Gi"
This policy enforces two critical requirements:
- All containers must have CPU and memory limits defined
- Memory limits cannot exceed 8Gi
The CEL expressions use familiar functional programming patterns like all()
to iterate over collections, making the logic both readable and powerful.
Why This Matters: The Kubernetes CEL Native Story
Understanding how CEL fits into the broader Kubernetes ecosystem helps appreciate why this standardization matters. Consider how the same validation logic can now work across different contexts.
CRD Validation Example:
apiVersion: apiextensions.k8s.io/v1kind: CustomResourceDefinitionmetadata: name: applications.platform.iospec: validation: openAPIV3Schema: properties: spec: properties: replicas: type: integer x-kubernetes-validations: - rule: "self <= 10" message: "replicas cannot exceed 10"
Kyverno ValidatingPolicy Example:
apiVersion: policies.kyverno.io/v1alpha1kind: ValidatingPolicymetadata: name: replica-limitspec: matchConstraints: resourceRules: - operations: ["CREATE", "UPDATE"] apiGroups: ["apps"] apiVersions: ["v1"] resources: ["deployments"] validations: - expression: "object.spec.replicas <= 10" message: "replicas cannot exceed 10"
Notice how the core CEL expression object.spec.replicas <= 10
remains identical across all three contexts. This consistency dramatically reduces the learning curve and enables teams to reuse validation logic.
Advanced Pattern: Cross-Resource Validation
CEL’s power becomes evident when handling complex validation scenarios that require external data or cross-resource relationships:
apiVersion: policies.kyverno.io/v1alpha1kind: ValidatingPolicymetadata: name: namespace-resource-quotasspec: matchConstraints: resourceRules: - operations: ["CREATE", "UPDATE"] apiGroups: ["apps"] apiVersions: ["v1"] resources: ["deployments", "statefulsets"] variables: - name: configs expression: > resource.Get("v1", "configmaps", "platform", "team-configs") validations: - expression: | has(object.metadata.labels.team) && object.metadata.labels.team in variables.configs.data && object.spec.replicas <= int(variables.configs.data[object.metadata.labels.team]) message: "Replica count exceeds team quota defined in platform/team-configs" - expression: | has(object.metadata.labels.environment) && object.metadata.labels.environment in ['dev', 'staging', 'prod'] message: "Must specify valid environment label: dev, staging, or prod"
This policy demonstrates advanced CEL patterns:
- External parameter references using
paramKind
andparamRef
- Data validation against external ConfigMap values
- Multi-expression validation with distinct error messages
- Complex boolean logic combining multiple conditions
The referenced ConfigMap might look like:
apiVersion: v1kind: ConfigMapmetadata: name: team-configs namespace: platformdata: frontend: "5" backend: "10" data: "3"
ImageValidatingPolicy in Action
Kyverno 1.14 also introduces ImageValidatingPolicy for specialized image security validation:
# https://github.com/kyverno/policies/blob/main/other/verify-image-ivpol/verify-image-ivpol.yamlapiVersion: policies.kyverno.io/v1alpha1kind: ImageValidatingPolicymetadata: name: verify-image-ivpol annotations: policies.kyverno.io/title: Verify Image policies.kyverno.io/category: Software Supply Chain Security, EKS Best Practices policies.kyverno.io/severity: medium policies.kyverno.io/subject: Pod policies.kyverno.io/minversion: 1.14.0 policies.kyverno.io/description: >- Using the Cosign project, OCI images may be signed to ensure supply chain security is maintained. Those signatures can be verified before pulling into a cluster. This policy checks the signature of an image repo called ghcr.io/kyverno/test-verify-image to ensure it has been signed by verifying its signature against the provided public key. This policy serves as an illustration for how to configure a similar rule and will require replacing with your image(s) and keys.spec: webhookConfiguration: timeoutSeconds: 30 evaluation: background: enabled: false validationActions: [Deny] matchConstraints: resourceRules: - apiGroups: [""] apiVersions: ["v1"] operations: ["CREATE", "UPDATE"] resources: ["pods"] matchImageReferences: - glob : "docker.io/mohdcode/kyverno*" attestors: - name: cosign cosign: key: data: | -----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE6QsNef3SKYhJVYSVj+ZfbPwJd0pv DLYNHXITZkhIzfE+apcxDjCCkDPcJ3A3zvhPATYOIsCxYPch7Q2JdJLsDQ== -----END PUBLIC KEY----- validations: - expression: >- images.containers.map(image, verifyImageSignatures(image, [attestors.cosign])).all(e ,e > 0) message: >- failed the verification
This policy combines signature verification with CEL-based attestation validation, ensuring:
- Images are signed with the specified public key
- Build provenance follows SLSA standards
- Dependencies meet stability requirements
Migration Guide: From ClusterPolicy to ValidatingPolicy
Migrating existing ClusterPolicy resources to the new ValidatingPolicy type often simplifies your policy definitions. Here’s a before/after comparison:
Before (ClusterPolicy):
apiVersion: kyverno.io/v1kind: ClusterPolicymetadata: name: require-labelsspec: rules: - name: require-labels match: any: - resources: kinds: - Deployment validate: failureAction: Enforce message: "Environment label must be dev, staging, or prod" anyPattern: - metadata: labels: environment: "dev" # Changed label key to 'environment' to match message - metadata: labels: environment: "staging" - metadata: labels: environment: "prod"
After (ValidatingPolicy):
apiVersion: policies.kyverno.io/v1alpha1kind: ValidatingPolicymetadata: name: require-labelsspec: matchConstraints: resourceRules: - operations: ["CREATE", "UPDATE"] apiGroups: ["apps"] apiVersions: ["v1"] resources: ["deployments"] validations: - expression: "has(object.metadata.labels.team) && object.metadata.labels.team != ''" message: "Deployments must have a team label" - expression: "has(object.metadata.labels.environment) && object.metadata.labels.environment in ['dev', 'staging', 'prod']" message: "Environment label must be dev, staging, or prod"
Migration Benefits:
- Reduced complexity: Single rule instead of multiple rules
- Better performance: CEL compilation vs pattern matching
- Clearer logic: Explicit boolean expressions vs pattern inference
- Enhanced readability: Self-documenting validation conditions
Performance Implications
CEL’s compilation model provides significant performance advantages over traditional validation approaches. Kyverno maintainers report substantial improvements in policy evaluation speed, though specific benchmarks vary by workload:
ClusterPolicy Pattern Matching:
- Parse YAML patterns at runtime
- Recursive object traversal for each validation
- Variable performance depending on pattern complexity
ValidatingPolicy CEL:
- Compile expressions once at policy load
- Direct object field access via compiled bytecode
- Faster execution for complex validation logic
Real-world Impact:
- Measurably faster policy evaluation
- Reduced CPU usage during peak admission loads
- Better cluster responsiveness under policy-heavy workloads
For clusters with hundreds of policies and high pod creation rates, these performance improvements directly translate to reduced latency and improved user experience.
Getting Started Checklist
Ready to adopt Kyverno 1.14’s ValidatingPolicy? Follow this implementation path:
- Upgrade Kyverno to version 1.14 or later
- Start with simple policies using basic CEL expressions
- Gradually migrate existing ClusterPolicy resources
- Leverage external parameters for dynamic validation
- Implement ImageValidatingPolicy for supply chain security
- Monitor performance improvements in your cluster
The convergence of CEL across Kubernetes and Kyverno represents more than just a technical upgrade - it’s a fundamental shift toward a unified policy ecosystem. By adopting these new ValidatingPolicy types, you’re investing in skills and patterns that will serve your platform engineering efforts across the entire Kubernetes landscape.
Start experimenting with CEL today, and experience the power of unified validation logic across your cloud native stack.
Related Articles

Lazyjournal: A Log Viewer for Cloud Native Environments
Centralized log access for developers and system administrators


Production Deployments with Dagger and GitLab CI
Containerized builds, automatic previews, and zero-config deployments


Federated GraphQL for Microservice Architecture
Seamless Data Access Across Service Boundaries Without Compromise
