Best Practices for Optimizing CEL in Kyverno


Kyverno’s integration with Common Expression Language (CEL), combined with its similarity with the validatingAdmissionPolicy
structure in validatingPolicy
and imageValidatingPolicy
, empowers Kubernetes administrators to craft robust, declarative policies. Yet, unoptimized CEL expressions can grow verbose, and managing policies across diverse resource types can add complexity. This post details best practices to streamline CEL policies using optionals, variables, Kyverno’s built-in functions, and techniques for multi-resource checks—while showcasing how Kyverno’s autogen feature simplifies the process.
The Challenge: Redundancy and Multi-Resource Complexity
CEL policies often involve repetitive checks like has()
for field existence, leading to cluttered expressions. For example:
object.spec.containers.all(container,
has(container.securityContext) &&
has(container.securityContext.allowPrivilegeEscalation) &&
container.securityContext.allowPrivilegeEscalation == false)
Additionally, applying policies across multiple resource kinds (e.g., Pods, Deployments) can lead to conflicts or unnecessary execution. Let’s tackle both issues efficiently.
Best Practices
1. Use Optionals to Simplify Field Access
CEL’s optional chaining (.?
) and default values (.orValue()
) eliminate redundant has()
checks, making expressions concise and clear.
Before:
has(container.securityContext) &&
has(container.securityContext.allowPrivilegeEscalation) &&
container.securityContext.allowPrivilegeEscalation == false
After:
container.?securityContext.?allowPrivilegeEscalation.orValue(true) == false
Why it works: Optional chaining navigates nested fields safely, with .orValue()
providing a fallback for absent fields.
2. Define Variables for Reusable Logic
Variables consolidate complex or repeated logic into a single definition, reducing duplication and enhancing maintainability. Note that variables cannot be defined within loops like all.containers()
, and loop-specific logic must stay within its scope.
Before:
(object.spec.containers +
(has(object.spec.initContainers) ? object.spec.initContainers : []) +
(has(object.spec.ephemeralContainers) ? object.spec.ephemeralContainers : []))
After:
variables:
- name: ctnrs
expression: >-
object.spec.containers +
object.spec.?initContainers.orValue([]) +
object.spec.?ephemeralContainers.orValue([])
validations:
- expression: variables.ctnrs.all(c, ...)
Why it works: The variable centralizes logic for reuse and easier updates.
Note: These features are currently supported as alpha in the main branch and are slated for full support in the Kyverno 1.14 release.
3. Leverage Kyverno’s Built-In Functions
Kyverno provides built-in functions in its library to quickly access resources, removing the need for repetitive CEL expressions. Functions such as imagedata.Get()
, resource.Get()
, resource.List()
, parseServiceAccount()
, http.Get
, http.Post
and the Image
function—which can inspect image internals like architecture, digest, manifest, and more—along with globalContext.Get()
and others, streamline policy creation in validatingPolicy
and imageValidatingPolicy
, while maintaining structural alignment with validatingAdmissionPolicy
. These functions minimise redundancy, accelerate resource retrieval, and integrate smoothly with custom variables and expressions, enhancing policy development efficiency.
4. Handle Multi-Resource Checks and Conflicts
When a policy applies to multiple resource kinds (e.g., Pods, Deployments, StatefulSets), expressions may conflict or execute unnecessarily. For example, checking object.spec.containers
works for Pods but fails for Deployments, where the path is object.spec.template.spec.containers
. Blindly applying expressions across kinds can lead to errors or unintended behavior.
Solution: Scope Expressions by Resource Kind
You can use CEL to conditionally execute checks based on the resource’s kind
, ensuring expressions only run when relevant. The object.kind
field lets you target specific resources.
Example:
object.kind == "Pod" ?
object.spec.containers.all(container,
container.?securityContext.?allowPrivilegeEscalation.orValue(true) == false) :
object.kind == "Deployment" ?
object.spec.template.spec.containers.all(container,
container.?securityContext.?allowPrivilegeEscalation.orValue(true) == false) :
true
Why it works: The ternary operator (?:
) checks object.kind
and applies the appropriate expression, defaulting to true
(no enforcement) for unmatched kinds, avoiding conflicts.
With Variables:
variables:
- name: ctnrs
expression: >-
object.kind == "Pod" ? object.spec.containers :
object.kind == "Deployment" ? object.spec.template.spec.containers : []
validations:
- expression: variables.ctnrs.all(c, c.?securityContext.?allowPrivilegeEscalation.orValue(true) == false)
Kyverno’s Autogen Simplifies This
Fortunately, Kyverno’s autogen feature reduces the need for such manual scoping. When you write a policy for a single resource kind (e.g., Pod or Deployment), Kyverno can automatically generate equivalent policies for related kinds like CronJobs, StatefulSets, or DaemonSets. This means you can focus on crafting a single, well-optimized expression, and Kyverno handles the multi-resource adaptation for you—no need to manually account for every kind.
5. Keep Names Descriptive
Use meaningful variable names (e.g., ctnrs
for containers, secCtx
for security context) to make policies intuitive and self-documenting.
6. Test Expressions First
Validate your CEL expressions in the CEL Playground and test in policies in Kyverno Playground before deploying them. This ensures correctness, especially for multi-resource logic.
Conclusion
By combining optionals, variables, Kyverno’s built-in functions, and either kind-specific checks or its autogen feature, you can craft CEL policies that are elegant, efficient, and conflict-free. Apply these best practices in your Kyverno workflows to enforce Kubernetes policies with precision and ease. Test, refine, and deploy—your cluster will benefit from the clarity and control!
Subscribe to my newsletter
Read articles from Mohd Kamaal directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Mohd Kamaal
Mohd Kamaal
Open source enthusiast | Blogger