Happy Dussehra! May good always win. Happy Diwali! Wishing you a bright year ahead. Merry Christmas! Warm wishes from Vizag. Happy New Year! Here’s to a great year ahead.

DevOps · CNCF daily

Open Policy Agent and Gatekeeper: One Policy Language for Kubernetes Admission and Everything Else

CNCF Graduated. OPA is a general-purpose policy engine with the Rego language; Gatekeeper, a separate project in the same organisation, runs it as a Kubernetes admission controller with constraint templates, audit, mutation and external data. Covers install, real policies, safe rollout, testing, operations and a comparison with Kyverno.

28 min read

CNCF Graduated

  • Open Policy Agent
  • Gatekeeper
  • Rego
  • Kubernetes
  • gator
CNCF project page

Open Policy Agent (OPA, pronounced “oh-pa”) is a general-purpose policy engine. A program sends it structured data (a Kubernetes admission request, a Terraform plan, an HTTP request, a CI job description), OPA evaluates that data against policies written in a declarative language called Rego, and it answers with a decision: allow or deny, or any structured result such as a list of violations. OPA does not enforce anything itself; it separates the decision from the enforcement point, so the same language and tooling can govern many systems. Gatekeeper is the project that brings this to Kubernetes. It is a separate repository in the same GitHub organisation (open-policy-agent/gatekeeper) that runs as an admission controller and turns policies into Kubernetes resources: reusable ConstraintTemplates that hold the logic, and Constraints that apply them with parameters. OPA is a CNCF graduated project (accepted 29 March 2018, incubating 2 April 2019, graduated 29 January 2021). Gatekeeper is listed in the CNCF Landscape as its own item, “OPA/Gatekeeper”, with no maturity level of its own, so the graduated status belongs to OPA and Gatekeeper is its Kubernetes integration. This brief explains both from the problem they solve to installing Gatekeeper, writing real policies, rolling them out without breaking a cluster, testing them, and operating and securing the result, with an honest comparison to Kyverno.

CNCF daily · Sprint 4, post 9 of 15 · Security & Compliance

At a glance

Category Provisioning › Security & Compliance
CNCF status Open Policy Agent: Graduated (accepted 29 March 2018, incubating 2 April 2019, graduated 29 January 2021). Gatekeeper: a Landscape item inside the OPA organisation, with no separate CNCF maturity level
Origin OPA was created at Styra in 2016 and donated to the CNCF in 2018
Written in Go
License Apache 2.0
Latest OPA release v1.22.0, tagged 29 October 2026 (the 1.x line uses Rego v1 syntax by default)
Latest Gatekeeper release v3.23.1, tagged 27 August 2026; a v3.24.0 beta was tagged on 13 July 2026
Policy languages Rego (OPA), and CEL in Gatekeeper templates through the Kubernetes Validating Admission Policy engine
Gatekeeper resources ConstraintTemplate, constraints created from each template, mutators (Assign, AssignMetadata, ModifySet, AssignImage), Config, SyncSet, Provider, ExpansionTemplate
Enforcement actions deny (default), warn, dryrun and scoped, per enforcement point
Test tools opa test for Rego unit tests, gator verify and gator test for Gatekeeper policies
Install Helm chart or the release manifest

The problem OPA and Gatekeeper solve

Every organisation has rules: images must come from an approved registry, every workload needs an owner label, containers must not run privileged, ingress hosts must be unique, and a Terraform plan must not open a database to the internet. Written in a wiki, such rules are followed unevenly. Hard-coded into each tool, they are written in a different way every time: a Kubernetes admission webhook in Go, a script in CI, a custom check in an API gateway. Each copy drifts, none of them can be tested in the same way, and none can answer the audit question “which resources already break this rule?”

OPA’s answer is to treat policy as code that lives outside the enforcing program:

  • One language. Rego expresses rules over JSON-shaped data, whatever produced it.
  • One decision interface. The caller sends input and receives a decision; OPA does not care whether the caller is Kubernetes, Envoy, a CI job or a microservice.
  • Testable and reviewable. Policies are plain text files with unit tests, so they go through Git review and CI.

Gatekeeper adds what Kubernetes needs on top of the bare engine. Policies become Kubernetes resources, so they are applied, versioned and rolled out like the rest of cluster configuration. A shared, parameterised library of templates exists. Violations are recorded by an audit loop for the resources that were created before the policy existed. And a dry-run mode lets you see what a policy would block before it blocks anything.

Key concepts

  • Rego. OPA’s declarative query language. A rule says “this is true if all these conditions hold”. You write what must hold, not the steps to check it.
  • Input, data and policy. input is what the caller sends. data is extra context loaded into OPA. A policy is a set of Rego rules, organised in packages.
  • Rego v1. OPA 1.0 made the newer syntax the default: rule bodies use the if keyword, and sets and multi-value rules use contains. Gatekeeper still defaults to the older v0 syntax in templates and accepts v1 as an opt-in per template (from Gatekeeper 3.19).
  • ConstraintTemplate. A Gatekeeper resource that defines a new kind of policy. It declares the name of the constraint kind it creates, the schema for that kind’s parameters, and the Rego (or CEL) that decides violations.
  • Constraint. An instance of a template’s kind. It says which resources it covers (match), the parameter values (parameters) and what to do on a violation (enforcementAction).
  • violation rule. The Rego entry point in a Gatekeeper template. Each object it produces is one violation, with a message and optional details. No violations means the request is allowed.
  • Audit. A periodic loop that evaluates existing resources against all constraints and writes the results into each constraint’s status.
  • Enforcement action and enforcement point. What happens on a violation, and where the constraint is evaluated: the admission webhook, gator in CI, the audit loop, or Kubernetes’ own Validating Admission Policy.
  • Mutator. A separate resource kind that changes an object during admission, for example adding a label or setting imagePullPolicy.
  • Provider. An in-cluster service that Gatekeeper can call for external data, such as image signature results.

Architecture

Gatekeeper architecture: users and CI send create and update requests to the Kubernetes API server, which sends an admission review to the Gatekeeper webhook; the webhook evaluates the request with the policy engine, which runs OPA Rego or CEL using constraint templates and constraints stored as Kubernetes resources; a policy repository applies those resources; the audit component evaluates existing resources with the same rules and writes violations to constraint status, logs and metrics.

Policies are ordinary Kubernetes resources; the webhook judges new requests and the audit loop judges what already exists, with the same rules. Blue is a user or repository, slate Kubernetes, purple a Gatekeeper component, green the policy resources and orange the results. Open the zoomable diagram, or download it as SVG or PNG.

Component Responsibility
Users and CI Create and change Kubernetes resources, and run gator against manifests before they reach a cluster
Kubernetes API server Calls the Gatekeeper validating and mutating webhooks for each matching request
Gatekeeper webhook The controller-manager pods (three replicas by default in the Helm chart) that receive admission reviews and answer allow, warn or deny, and apply mutators
Policy engine Embeds the OPA library to run Rego, and the Kubernetes CEL engine for CEL-based templates
Policy resources ConstraintTemplate and constraint objects stored in the cluster; the engine reads them as rules and parameters
Policy repository The Git repository, reviewed and tested, from which GitOps applies templates and constraints
Audit A separate deployment that lists existing resources on an interval (60 seconds by default) and evaluates them with the same constraints
Results Violations in each constraint’s status, JSON audit logs and Prometheus metrics

Design principle. Policy is data in the cluster, and the engine is generic. Gatekeeper’s authors write a template once, with Rego in it, and platform teams then create many constraints from it with different parameters and scopes, so most users never edit Rego.

From a tested policy to a decision and an audit report

Gatekeeper flow in six steps: the policy author writes test cases and runs gator verify; the policy is applied as a constraint, first in dry-run; a client creates a resource; the Kubernetes API calls the Gatekeeper webhook with an admission review; the webhook evaluates the request against the constraints and allows, warns or denies; the audit component evaluates existing resources against the same constraints and records violations.

  1. Test. The author writes a template and test cases, and gator verify checks them in CI.
  2. Apply. The template and a constraint are applied to the cluster, with the constraint first in dryrun or warn mode.
  3. Create. A user or pipeline creates or updates a resource.
  4. Review. The Kubernetes API server calls the Gatekeeper webhook with an admission review.
  5. Evaluate. The webhook evaluates the object against the matching constraints. The result is allow, allow with warnings, or deny with the violation messages.
  6. Audit. On its interval, the audit loop evaluates existing resources against the same constraints and records violations in the constraint status.

Recap: test before applying, observe before enforcing, and let audit find what the webhook never saw.

Rego in ten minutes

Rego looks unusual at first because it is not a procedural language. Three ideas cover most of what you need.

Rules are conditions that must all hold. Every expression in a rule body is a condition, and the rule is true only if all of them are true. For a simple rule in Rego v1 syntax:

package example

default allow := false

allow if {
    input.method == "GET"
    input.user.role == "reader"
}

default supplies a value when no rule body matches, so allow is false unless the conditions hold.

Sets and objects are built from rules. A rule can contribute elements to a set, which is how Gatekeeper’s violation works:

package example

deny contains msg if {
    some container in input.spec.containers
    endswith(container.image, ":latest")
    msg := sprintf("container %v uses the latest tag", [container.name])
}

some container in ... iterates, and each iteration that satisfies the body adds one message. There is no loop to write and no return statement: the set holds every message for which the conditions held.

Undefined means false. A reference to a field that does not exist makes the expression undefined, so the rule body stops, and no error is thrown. This is why a missing label does not crash a policy, and also why a typo in a field name makes a rule silently never match. Tests catch that, which is the main reason to write them.

Other things you will meet: not for negation, every for “for all”, comprehensions such as {x | ...} to build sets, built-in functions (sprintf, startswith, regex.match, count) and with to override input in tests. The OPA documentation’s policy language page covers them in full, and the online Rego Playground lets you try policies against sample input.

OPA outside Kubernetes

Gatekeeper is one way to use OPA, and a short tour of the others helps explain why Rego is worth learning once:

  • Server. opa run --server exposes a REST API. An application sends input to POST /v1/data/<path> and receives the decision. By default OPA binds to localhost, so exposing it takes a deliberate --addr setting, and it should then be paired with TLS and authentication.
  • Library. In Go, OPA can be embedded in the application, which removes the network hop.
  • WebAssembly. Policies can be compiled to Wasm and evaluated by any Wasm runtime.
  • Bundles and decision logs. A running OPA can download policy and data bundles from a remote HTTP server and report decision logs and status, which is how fleets of OPA instances are managed.
  • Envoy and other integrations. OPA has documented integrations for Envoy and Istio authorisation, Kafka, Docker and SSH authorisation, and Terraform and CI checks, using the same Rego policy format.
  • CLI. opa eval, opa test, opa fmt and opa build are the daily tools for writing and checking policy.

Install Gatekeeper

Gatekeeper needs cluster-admin rights to install, because it registers webhooks and CRDs. Use Helm and pin a version.

helm repo add gatekeeper https://open-policy-agent.github.io/gatekeeper/charts
helm repo update
helm install gatekeeper gatekeeper/gatekeeper \
  --namespace gatekeeper-system --create-namespace \
  --version <chart-version>

Choose the chart version that matches the Gatekeeper release you have tested (the repository’s default branch carries a 3.24 beta chart, so read the chart’s README for the value names of your version). The alternative is applying the release manifest with kubectl apply -f. Then verify:

kubectl get pods -n gatekeeper-system
kubectl get crd | grep gatekeeper.sh

You should see the controller-manager pods and an audit pod, and CRDs for templates, constraints, mutators, config and providers. The chart’s defaults worth knowing:

Helm value Default Meaning
replicas 3 Number of controller-manager (webhook) pods
auditInterval 60 Seconds between audit runs; 0 disables audit
constraintViolationsLimit 20 Maximum violations written to each constraint’s status
validatingWebhookFailurePolicy Ignore What the API server does if the webhook is unreachable
validatingWebhookTimeoutSeconds 3 Webhook timeout
mutatingWebhookFailurePolicy Ignore Same for the mutating webhook
enableExternalData true Allows the external data feature
enableK8sNativeValidation true Allows CEL templates and Validating Admission Policy integration
logDenies false Log every denied request in the webhook log
controllerManager.exemptNamespaces empty Namespaces allowed to carry the ignore label

A first policy: required labels

Start with a template, written here in Rego v1 syntax, which requires the code form of the target:

apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
  name: k8srequiredlabels
spec:
  crd:
    spec:
      names:
        kind: K8sRequiredLabels
      validation:
        openAPIV3Schema:
          type: object
          properties:
            labels:
              type: array
              items:
                type: string
  targets:
    - target: admission.k8s.gatekeeper.sh
      code:
        - engine: Rego
          source:
            version: "v1"
            rego: |
              package k8srequiredlabels

              violation contains {"msg": msg, "details": {"missing_labels": missing}} if {
                provided := {label | input.review.object.metadata.labels[label]}
                required := {label | some label in input.parameters.labels}
                missing := required - provided
                count(missing) > 0
                msg := sprintf("you must provide labels: %v", [missing])
              }

Applying a template makes Gatekeeper create a new CRD for the kind K8sRequiredLabels, with a parameters schema built from the template. The schema must be structural: every field needs a type, or the API server rejects the template. Now a constraint that uses it, in dry-run mode first:

apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredLabels
metadata:
  name: namespaces-need-owner
spec:
  enforcementAction: dryrun
  match:
    kinds:
      - apiGroups: [""]
        kinds: ["Namespace"]
    excludedNamespaces: ["kube-*", "gatekeeper-system"]
  parameters:
    labels: ["owner"]

The input.review.object is the object under review, and input.parameters is what the constraint supplied. Within a few audit intervals, kubectl get k8srequiredlabels namespaces-need-owner -o yaml shows status.violations listing every existing namespace without an owner label, and status.totalViolations shows the full count. Nothing has been blocked yet, which is the point.

The same idea in CEL

When a rule is a simple check on one object, the template can use Kubernetes’ CEL expression language through the K8sNativeValidation engine, with no Rego:

  targets:
    - target: admission.k8s.gatekeeper.sh
      code:
        - engine: K8sNativeValidation
          source:
            validations:
              - expression: 'has(object.metadata.labels) && variables.params.labels.all(l, l in object.metadata.labels)'
                messageExpression: '"missing required label, requires all of: " + variables.params.labels.join(", ")'

Gatekeeper evaluates CEL in the webhook, in the audit loop and in gator, and can generate native Kubernetes ValidatingAdmissionPolicy resources so the API server evaluates them in-process. Use CEL for simple checks, and Rego when you need loops over lists, referential data (other objects in the cluster), external data or helper libraries. If a template defines both engines, only the CEL one is evaluated, so define one per template.

Examples from the policy library

You rarely write common policies from scratch. The gatekeeper-library repository provides templates and sample constraints for allowed repositories, required labels, container limits, privileged containers, host networking, unique ingress hosts and the Pod Security Standards. The gator policy command (alpha, from v3.22) searches and installs them:

gator policy update
gator policy search labels
gator policy install --bundle pod-security-baseline --enforcement-action=warn

A bundle installs the templates and ready-made constraints, and --enforcement-action=warn is the safe way to start.

Match, parameters and scope

The match block of a constraint decides which requests it sees. All top-level matchers must hold:

Matcher Use
kinds API groups and kinds to cover, such as Pod or Namespace
scope Cluster, Namespaced or *
namespaces, excludedNamespaces Names, with prefix globs such as kube-*
labelSelector Select objects by their labels
namespaceSelector Select by the labels of the containing namespace
name One object name or a prefix glob

Parameters are free-form within the template’s schema, so one template serves many teams: a constraint for the payments namespace can allow one registry, and another for the data namespace can allow two.

Audit before enforce

The safest rollout has four stages, and Gatekeeper supports each natively through enforcementAction:

  1. dryrun. The constraint evaluates and records violations through audit, and the webhook never blocks. Read the violations, fix the real problems and decide which are exceptions.
  2. warn. The API response carries a warning that kubectl prints, so users see the problem and nothing is blocked. This is where developers learn about the rule.
  3. deny. The default. The request is rejected with the violation messages. Switch to it per constraint when the audit is clean.
  4. scoped. For different behaviour per enforcement point, for example warn at admission but deny in gator in CI. The audit point must be listed explicitly.
spec:
  enforcementAction: scoped
  scopedEnforcementActions:
    - action: warn
      enforcementPoints:
        - name: "validation.gatekeeper.sh"
    - action: deny
      enforcementPoints:
        - name: "gator.gatekeeper.sh"

Do not skip to deny for a cluster-wide constraint. A policy that blocks kube-system components or an operator’s own pods can stop a cluster from healing itself.

Exemptions

Exemptions come in layers, from narrow to broad, and the narrow ones are almost always right.

  • Per constraint. match.excludedNamespaces, label selectors or name globs inside the constraint itself.
  • Cluster-wide with the Config resource. The resource must be named config in the gatekeeper-system namespace. It excludes namespaces from specific processes (audit, webhook, sync, mutation-webhook, or *):
apiVersion: config.gatekeeper.sh/v1alpha1
kind: Config
metadata:
  name: config
  namespace: gatekeeper-system
spec:
  match:
    - excludedNamespaces: ["kube-*"]
      processes: ["*"]
    - excludedNamespaces: ["ci-sandbox"]
      processes: ["webhook"]
  • Skipping the webhook entirely. A namespace labelled admission.gatekeeper.sh/ignore bypasses the webhook, but only if Gatekeeper was also told, with --exempt-namespace (or the Helm controllerManager.exemptNamespaces), that the namespace may carry the label. That second step exists so that permission to edit a namespace does not equal permission to exempt it from policy. These exempted resources are still audited unless excluded there too.
  • In the template. Rego can read parameters such as an exemptImages list, and the libs mechanism supports shared helper functions, so one template handles a reviewed list of exceptions without code changes.

Record every exemption with the reason and an expiry in Git. Unreviewed exemptions are where policy programmes decay.

Mutation

Mutators change objects during admission, before validation sees the final result. They use their own resources and not templates:

apiVersion: mutations.gatekeeper.sh/v1
kind: Assign
metadata:
  name: pull-always
spec:
  applyTo:
    - groups: [""]
      kinds: ["Pod"]
      versions: ["v1"]
  match:
    scope: Namespaced
    kinds:
      - apiGroups: ["*"]
        kinds: ["Pod"]
    excludedNamespaces: ["kube-system"]
  location: "spec.containers[name:*].imagePullPolicy"
  parameters:
    assign:
      value: Always

The four kinds are AssignMetadata (labels and annotations, with stricter rules), Assign (anything else), ModifySet (add or remove list entries such as container arguments) and AssignImage (change the registry, repository, tag or digest of an image). applyTo is required except for AssignMetadata, and it does not accept globs, because Gatekeeper uses it to detect mutators that disagree about a kind’s schema. Mutators are stable since Gatekeeper 3.10. Keep them few and deterministic: two mutators that keep changing each other’s output create confusing behaviour, and a mutator that changes what a validation constraint sees is hard to debug. The metric gatekeeper_mutator_conflicting_count reports conflicts.

External data

Some decisions need facts that do not live in the cluster: whether an image is signed, whether a tag resolves to a digest, a directory lookup. Rego’s http.send can call out, but Gatekeeper’s documentation warns that it lets template authors reach the in-cluster network and creates injection risks, and mutation cannot use it. The external data feature (beta) solves this with a registered Provider:

apiVersion: externaldata.gatekeeper.sh/v1beta1
kind: Provider
metadata:
  name: image-check
spec:
  url: https://image-check.provider-ns:8090/validate
  timeout: 3
  caBundle: <base64 CA certificate>

A template then calls external_data({"provider": "image-check", "keys": images}) and receives results and errors per key. Gatekeeper makes the calls over mutual TLS, handles caching and batching, and allows only registered hosts. Community providers exist for Ratify, Cosign and GitHub artifact attestations. Treat external data as a latency-sensitive dependency: for mutation, the effective timeout is capped by the remaining admission deadline, and a slow provider on the admission path can cause failures in any mode, and the audit-only enforcement point is a reasonable place for slow checks.

Referential policies and data replication

Some rules need other objects, for example “no two ingresses may use the same host”. Gatekeeper replicates chosen kinds into the policy engine, readable in Rego as data.inventory. Replication is configured with SyncSet resources (alpha) or the Config resource’s sync section:

apiVersion: syncset.gatekeeper.sh/v1alpha1
kind: SyncSet
metadata:
  name: sync-ingresses
spec:
  gvks:
    - group: "networking.k8s.io"
      version: "v1"
      kind: "Ingress"

Replicated data costs memory in the Gatekeeper pods, so sync only the kinds a policy needs. Also note a Gatekeeper limitation: a referential rule at admission sees the cached state, which is eventually consistent, so two requests arriving at the same moment can both pass.

Testing policies

Policies are code and need tests at two levels.

Rego unit tests with opa test check the logic in isolation. Rules whose names start with test_ are run, and with input as {...} supplies the data:

package example_test

import data.example

test_latest_tag_denied if {
    msgs := example.deny with input as {"spec": {"containers": [{"name": "web", "image": "shop/web:latest"}]}}
    count(msgs) == 1
}

test_pinned_tag_allowed if {
    msgs := example.deny with input as {"spec": {"containers": [{"name": "web", "image": "shop/web:1.4.2"}]}}
    count(msgs) == 0
}
opa test . -v
opa fmt --write .

gator verify tests complete Gatekeeper policies: a template, a constraint and sample objects, with the exact result asserted. A suite file declares tests and cases:

kind: Suite
apiVersion: test.gatekeeper.sh/v1alpha1
tests:
  - name: required-labels
    template: template.yaml
    constraint: constraint.yaml
    cases:
      - name: labelled
        object: samples/namespace-with-owner.yaml
        assertions:
          - violations: no
      - name: unlabelled
        object: samples/namespace-without-owner.yaml
        assertions:
          - violations: yes
            message: "you must provide labels"
gator verify ./...
gator test -f manifests/ -f policy/

gator test evaluates a set of manifests against templates and constraints and returns a non-zero exit status on violations, which suits a CI step that checks rendered Helm or Kustomize output before it reaches a cluster. Run both in the same pipeline that deploys the policies, so a template that does not compile never reaches the cluster. A case can also load an inventory of objects for referential rules.

Security

  • Gatekeeper is a high-value target. It sees every admission request and can block them, so restrict who can change templates, constraints, mutators, Config and Provider resources with RBAC, and manage them through reviewed Git changes.
  • Fail-open is the default. The webhook’s failurePolicy is Ignore, so if Gatekeeper is down or unreachable, requests are admitted unchecked and audit has to catch the gap later. Setting Fail closes that hole but turns a Gatekeeper outage into a cluster-wide write outage, so only do it with three or more replicas, anti-affinity, a PodDisruptionBudget, correct resource limits, and namespace exemptions for the system components Gatekeeper itself needs.
  • Webhook scope. The default webhook skips namespaces carrying the admission.gatekeeper.sh/ignore label. Control who can set that label, and keep the --exempt-namespace list short.
  • Template authors are trusted. Rego in a template runs in the Gatekeeper process, and http.send, where allowed, reaches the network. Prefer the Provider model for external calls, and do not let untrusted tenants create templates.
  • Supply chain. Gatekeeper images carry SBOM and SLSA provenance attestations from v3.12.0, and vulnerabilities are reported to the project’s security mailing list. Pin image digests and verify them.
  • OPA server. If you run OPA as a service, bind it to localhost unless it must be reached, and then use TLS and authentication (--authentication=token or tls, and an authorisation policy), as the OPA security documentation describes.
  • Emergency recovery. If Gatekeeper blocks something essential, deleting the gatekeeper-validating-webhook-configuration object disables admission checks, and redeploying it restores them. Keep that step in your runbook and restrict who can do it.

Monitoring and day-2 operations

Metrics. Gatekeeper exposes Prometheus metrics, by default on port 8888. The ones worth alerting on:

Metric Why
gatekeeper_violations Audited violations by severity; the trend should fall after rollout
gatekeeper_audit_last_run_end_time A stale value means audit is failing or stuck
gatekeeper_audit_duration_seconds Audit runtime growing toward the interval is a scaling signal
gatekeeper_validation_request_count and gatekeeper_validation_request_duration_seconds Admission volume, outcomes and latency
gatekeeper_mutation_request_count and gatekeeper_mutator_conflicting_count Mutation load and conflicts
gatekeeper_constraint_templates, gatekeeper_constraints Policy inventory by status; template ingestion errors show here
gatekeeper_sync, gatekeeper_watch_manager_watched_gvk Replication health and what is being watched
gatekeeper_provider_error_count External data failures

Logs and events. The audit pod writes JSON logs with an event_type of violation_audited. Enable logDenies to log each denial in the webhook, and the alpha --emit-admission-events flag to emit a Kubernetes event for each admission violation.

Sizing and tuning.

  • Webhook and audit run in separate pods, so audit running out of memory does not affect admission.
  • Cap --max-serving-threads for large clusters; too many threads cause CPU throttling and jitter, and too few raise latency.
  • Audit memory grows with --constraint-violations-limit and with listing large kinds. Reduce --audit-chunk-size to limit peak memory, and use --audit-match-kind-only so audit lists only the kinds that constraints match.
  • The default limit of 20 violations per constraint status exists because API objects have a size limit in etcd; values above about 500 are not recommended. For full violation data, use logs or the export feature.
  • Keep templates efficient: avoid heavy nested iteration over large input collections, and measure with the engine’s metrics.

Upgrades. Gatekeeper’s CRDs are installed with the chart, and upgrades should go through a staging cluster. Read the release notes for changes to defaults and deprecated flags. For example, the --sync-vap-enforcement-scope flag is documented as deprecated and planned for removal in v3.24, and the documentation warns that exemption behaviour for generated Validating Admission Policies changed, so some namespaces can start being denied after an upgrade.

Gatekeeper and OPA versions

Gatekeeper embeds the OPA library, so its Rego behaviour follows the OPA version in its release, and the repository’s opa-versions page tracks the pairing. Because Gatekeeper still defaults to v0 syntax in templates, a policy copied from a recent OPA example with if and contains needs version: "v1" under source, or it will fail to compile. Run gator verify after every Gatekeeper upgrade.

Gatekeeper compared with Kyverno

Both projects do admission control, audit and mutation, and both are mature. They differ in language and in where each reaches. (There is a separate brief on Kyverno in this series.)

Gatekeeper with OPA Kyverno
Policy language Rego (and CEL for simple checks) YAML with pattern matching, and CEL in newer policy types
Learning curve Steeper; Rego is a new paradigm, but a library covers common rules Gentler for Kubernetes users, since policies look like Kubernetes resources
Reuse beyond Kubernetes Strong: the same Rego runs in Envoy, CI, Terraform checks and services Kubernetes-focused, with a JSON policy engine for other documents
Validate and mutate Yes, with separate resource kinds Yes, in one policy format
Generate resources No Kyverno-style generation of other resources; it validates and mutates Yes, a core feature, with clean-up policies
Image verification Through external data providers such as Ratify or Cosign Built in, with Cosign and Notation
Audit Constraint status, logs and metrics Policy reports in the Policy Report format
Testing opa test and gator verify The Kyverno CLI
Complex logic Rego handles joins, comprehensions and referential data well Possible, but less natural for deep logic

A practical rule: pick Kyverno when your team lives in Kubernetes YAML and wants generation and built-in image verification with low overhead. Pick Gatekeeper when you want one policy language across Kubernetes and other systems, when you have complex referential logic, or when your organisation already uses OPA. Many clusters also use Kubernetes’ built-in ValidatingAdmissionPolicy for the simple checks, and Gatekeeper can manage and generate those too.

Usage cases

  • Baseline guardrails. Required labels, approved registries, resource limits, no privileged containers and no host networking, rolled out as dryrun, then warn, then deny.
  • Uniqueness and referential rules. Ingress hosts or namespace names that must not collide, using replicated data.
  • Defaults through mutation. Adding labels, setting imagePullPolicy or routing images to an internal registry.
  • Supply-chain checks. Image signature and attestation checks through an external data provider.
  • Shift-left. gator in CI checks manifests against the same constraints before merge, and OPA checks Terraform plans and other configuration.
  • Compliance evidence. Audit results as a continuous report of which resources violate which rule.

The Sprint 4 pharmaceutical plant project uses Gatekeeper to put admission guardrails on its Kubernetes clusters on Google Cloud.

Project status

OPA is a CNCF graduated project with a steady release cadence; v1.22.0 is the latest tag as of 29 October 2026, and the 1.x line changed Rego’s default syntax, so old policies needed a migration. Its changelog shows ongoing work, including breaking changes to how bundles are rooted at their manifest file in the unreleased section, so read the release notes before upgrading a fleet. Recent Gatekeeper releases are v3.23.0 on 9 July 2026, v3.23.1 on 27 August 2026 and a v3.24 beta tagged on 13 July 2026. Recent Gatekeeper work has centred on CEL and Validating Admission Policy integration, with several features still alpha or beta (SyncSet, Config, gator policy, violation export and external data), so check each feature’s stated state before you depend on it.

Adoption

OPA’s ADOPTERS.md lists organisations that have spoken publicly about their use or added themselves. Entries include 2U, Appsflyer (authorisation for hundreds of microservices), Atlassian (API authorisation, with decision logs), bol.com (admission control on its Kubernetes clusters), BNY Mellon (sidecar authorisation) and Capital One (Kubernetes admission policies), among many others, with uses ranging from admission control to microservice authorisation and CI checks. These descriptions are the organisations’ own, collected in the project file. For Gatekeeper specifically, the project documentation and library are the best public record, and its adopters are not listed separately.

Alternatives

Solution Model Best suited to
Gatekeeper with OPA Admission controller with Rego and CEL constraint templates Teams that want one policy language across Kubernetes and other systems
Kyverno Kubernetes-native policies written in YAML or CEL Kubernetes teams that want low-friction policies, generation and image verification
Kubernetes ValidatingAdmissionPolicy Built-in CEL rules evaluated inside the API server Simple checks with no extra components to run
Pod Security Admission Built-in enforcement of the Pod Security Standards Basic pod hardening without custom policy
Kubewarden Policies compiled to WebAssembly Teams that want to write policies in a general-purpose language
Plain OPA with kube-mgmt OPA sidecar with a replication helper Teams that need OPA’s bundle and decision-log management features, as an alternative the OPA documentation itself describes for that case

Strengths and limitations

Strengths Limitations
One expressive language for Kubernetes, CI, services and gateways Rego has a real learning curve, and undefined-means-false hides typos
Templates and constraints separate policy logic from policy use Fail-open by default; failing closed needs careful availability work
Audit, dry-run and warn make safe rollout natural Several features are alpha or beta, and defaults change between releases
Mutation, external data and referential rules in one tool Referential data costs memory and is eventually consistent
opa test, gator and a shared policy library for testing and reuse Not a generate-resources tool, and image verification needs extra components

Recommendation

Adopt Gatekeeper where you want policy-as-code across more than Kubernetes, or where your policies need real logic. Start with the library, install it with three replicas, and run every new constraint as dryrun, then warn, then deny, with tests in CI through opa test and gator verify. Exempt system namespaces deliberately, keep exemptions in Git, decide the fail-open question in writing with availability measures behind it, and alert on audit freshness and webhook latency. If your needs are simple and your team prefers YAML, evaluate Kyverno or the built-in ValidatingAdmissionPolicy first, and use OPA directly for non-Kubernetes decisions where Rego pays off.

References: OPA documentation · Rego policy language · Policy testing · OPA security · OPA adopters · Gatekeeper documentation · Gatekeeper install · How to use Gatekeeper · ConstraintTemplates · Audit · Exempting namespaces · Mutation · External data · The gator CLI · Failing closed · Gatekeeper metrics · Gatekeeper policy library

Spotted something wrong or out of date?Suggest a correction. I review every suggestion before changing the page.