Introduction: The Policy Problem in Modern Infrastructure
Modern cloud-native infrastructure is complex by design. You have dozens of microservices, hundreds of Kubernetes namespaces, multiple cloud accounts, CI/CD pipelines deploying dozens of times per day, and teams of engineers who all need different levels of access to different resources. Governing all of this consistently — enforcing the rules that keep your systems secure, compliant, and predictable — is an enormous challenge.
For years, organizations tried to solve this problem with a patchwork of solutions: hard-coded authorization logic in application code, manually maintained firewall rules, bespoke admission scripts, and spreadsheet-driven compliance checklists. The result was policy fragmentation — different rules enforced differently in different places, with no single source of truth and no way to audit, test, or evolve policies systematically.
Open Policy Agent (OPA) was built to solve exactly this problem. It is a general-purpose, open-source policy engine that decouples policy decision-making from the systems that need to enforce those decisions. OPA has become the de facto standard for cloud-native policy enforcement — a CNCF-graduated project trusted by organizations ranging from Netflix and Intuit to the U.S. Department of Defense.
This guide is your comprehensive introduction to OPA: what it is, how it works, where it fits in your stack, and how to use it effectively in production.
What Is Open Policy Agent (OPA)?
Open Policy Agent is a general-purpose policy engine that enables unified, context-aware policy enforcement across your entire stack — from Kubernetes admission control to API gateway authorization, from Terraform plan validation to microservice-to-microservice access control.
OPA was created in 2016 by Styra and open-sourced shortly after. It joined the Cloud Native Computing Foundation (CNCF) as a sandbox project in 2018 and graduated to CNCF-graduated status in 2021 — the highest maturity level in the CNCF ecosystem, shared with projects like Kubernetes, Prometheus, and Envoy.
At its core, OPA does one thing exceptionally well: it takes a query and a context, evaluates them against a policy, and returns a decision. That decision is almost always structured JSON — typically a simple allow/deny, but it can be any structured data your calling system needs.
The OPA Model in Three Parts
Understanding OPA comes down to understanding three inputs it combines to reach a decision:
- Policy — The rules written in Rego, OPA's purpose-built declarative language. Policies define what is allowed and what is not.
- Data — The context OPA uses when evaluating policy. This might include user attributes, resource metadata, environment configuration, or any external data relevant to the decision.
- Query — The question being asked. Typically: "Is this action allowed?" combined with the specific input representing that action.
OPA evaluates the query against the policy and data, and returns a response. That's the entire model — and its simplicity is precisely what makes it so powerful and so portable across different systems.
Rego: The Language of OPA Policies
OPA policies are written in Rego (pronounced "ray-go"), a purpose-built declarative query language inspired by Datalog. Rego is designed for expressing complex access control and compliance logic in a way that is both human-readable and machine-evaluable.
Rego takes some getting used to if you are coming from imperative languages like Python or Go — it evaluates rules rather than executing statements sequentially. But once the mental model clicks, Rego becomes an extremely expressive tool for capturing nuanced policy logic.
Rego Fundamentals
A Rego policy is organized into a package, which is essentially a namespace. Rules within a package define logical propositions that OPA evaluates.
package example.authz
import future.keywords.if
import future.keywords.in
# Default deny
default allow := false
# Allow if user is an admin
allow if {
input.user.role == "admin"
}
# Allow read access to public resources
allow if {
input.action == "read"
input.resource.visibility == "public"
}
In this example:
package example.authznamespaces the policy.default allow := falsesets a safe default — deny everything unless explicitly allowed.- Each
allow if { ... }block defines a condition under which the action is permitted. inputis the query context — the JSON document representing the request being evaluated.
Rules, Variables, and Unification
Rego uses unification rather than assignment. Variables are bound when a condition is satisfied during evaluation. If a condition cannot be satisfied, the rule body is considered undefined — which is how Rego expresses "this rule does not apply."
package example.images
deny[msg] if {
container := input.spec.containers[_]
not startswith(container.image, "registry.internal.com/")
msg := sprintf("Container '%v' uses an untrusted image registry", [container.name])
}
Here, container := input.spec.containers[_] iterates over all containers in the input. The [_] is a wildcard index — Rego evaluates the rule for every element. If any container fails the registry check, a denial message is generated and added to the deny set.
Testing Rego Policies
OPA has a built-in unit testing framework. Test files follow the convention of importing the policy under test and defining rules prefixed with test_:
package example.images_test
import data.example.images
test_allow_trusted_image if {
not images.deny[_] with input as {
"spec": {
"containers": [{"name": "app", "image": "registry.internal.com/myapp:v1.0"}]
}
}
}
test_deny_untrusted_image if {
images.deny[_] with input as {
"spec": {
"containers": [{"name": "app", "image": "docker.io/nginx:latest"}]
}
}
}
Run tests with: opa test ./policies/ -v
This testability is one of OPA's most powerful features. Policies can be peer-reviewed, tested in CI/CD pipelines, and deployed with the same rigor as application code.
How OPA Works: The Architecture
OPA runs as a standalone service — a lightweight Go binary that exposes a REST API. It has no external dependencies and can be deployed as a sidecar container, a standalone pod, or an embedded library within a Go application.
Decision Flow
When a system wants a policy decision, it sends an HTTP POST to OPA's REST API:
POST /v1/data/example/authz/allow
Content-Type: application/json
{
"input": {
"user": {"name": "alice", "role": "developer"},
"action": "write",
"resource": {"type": "deployment", "namespace": "production"}
}
}
OPA evaluates the query against the loaded policy and data and responds:
{
"result": false
}
This interaction — query in, decision out — is the fundamental pattern OPA uses with every integrated system. The system calling OPA is the enforcer; OPA is purely the decision-maker. This separation is architecturally significant: it means OPA can be updated independently of the systems it governs, policies can be changed without redeploying applications, and the same policy engine can serve dozens of different systems.
Policy and Data Loading
OPA can load policies and data from multiple sources:
- Static files — policies bundled into the OPA deployment at startup.
- Bundles — compressed archives of policies and data served from a remote bundle server (OPA pulls and caches them automatically). This is the recommended pattern for production deployments.
- Push via REST API — policies and data can be pushed to OPA at runtime via its management API.
Bundles are the most robust pattern for managing policies at scale. OPA polls a bundle server (typically an object store like S3, GCS, or an Nginx server) on a configurable interval, downloads updated bundles, and hot-reloads them without restarting.
OPA in Kubernetes: Gatekeeper
The most widely known OPA integration is OPA Gatekeeper — a Kubernetes admission controller that uses OPA to enforce policies on every resource creation and update event in a cluster.
Gatekeeper is a purpose-built Kubernetes operator that wraps OPA and extends it with a Constraint Framework: a Kubernetes-native way of defining policy templates and instantiating policy instances from them.
ConstraintTemplates and Constraints
The Gatekeeper model has two layers:
ConstraintTemplate — defines the policy logic in Rego and declares the schema for policy parameters. It is written once by a policy author and creates a new Custom Resource Definition (CRD) in the cluster.
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: requiredlabels
spec:
crd:
spec:
names:
kind: RequiredLabels
validation:
openAPIV3Schema:
properties:
labels:
type: array
items:
type: string
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package requiredlabels
violation[{"msg": msg}] {
provided := {label | input.review.object.metadata.labels[label]}
required := {label | label := input.parameters.labels[_]}
missing := required - provided
count(missing) > 0
msg := sprintf("Missing required labels: %v", [missing])
}
Constraint — an instance of a ConstraintTemplate that specifies where and how the policy applies. Different teams can create different Constraints from the same template, each with their own parameters.
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: RequiredLabels
metadata:
name: require-app-and-owner-labels
spec:
match:
kinds:
- apiGroups: ["apps"]
kinds: ["Deployment"]
namespaces: ["production", "staging"]
parameters:
labels: ["app", "owner", "team"]
This Constraint enforces that all Deployments in the production and staging namespaces must have app, owner, and team labels. Any Deployment missing these labels is rejected at admission time with a clear error message.
Audit Mode
Gatekeeper's audit controller runs continuously, evaluating existing cluster resources against active Constraints. This catches resources that were created before a Constraint was applied, or that violated policy via direct etcd writes that bypassed admission control. Violations are reported in the Constraint's status field, making them discoverable via kubectl describe.
Mutation
In addition to validation, Gatekeeper supports mutation admission webhooks that automatically modify resources to bring them into compliance. For example, a MutatingPolicy can automatically add a default resource limit to any container that does not specify one, rather than rejecting the deployment outright. This is particularly useful for handling legacy workloads during a policy migration.
OPA Beyond Kubernetes: Cross-Stack Policy Enforcement
While OPA Gatekeeper is the most visible OPA use case, OPA's true power is its applicability across the entire technology stack.
API Authorization
OPA can serve as the authorization engine for REST APIs and GraphQL endpoints. Instead of embedding authorization logic in application code — where it becomes tightly coupled, hard to test, and inconsistent across services — applications delegate authorization decisions to OPA.
A typical pattern:
- An API gateway or application middleware intercepts the request.
- It constructs an input document: user identity, request path, HTTP method, query parameters.
- It sends the input to OPA via HTTP.
- OPA evaluates the policy and returns a decision.
- The middleware enforces the decision — allowing or denying the request.
This pattern is used at scale by companies like Netflix, Intuit, and Zalando to provide consistent, auditable authorization across hundreds of microservices.
Terraform Plan Validation with Conftest
Conftest is a tool that uses OPA and Rego to validate structured configuration files — including Terraform plans, Kubernetes manifests, Dockerfiles, and Helm charts — before they are applied.
In a GitOps or CI/CD workflow, Conftest runs as a pipeline step after terraform plan and before terraform apply. It evaluates the plan JSON against Rego policies and fails the pipeline if violations are found.
package terraform.aws.security
deny[msg] if {
resource := input.resource_changes[_]
resource.type == "aws_s3_bucket"
resource.change.after.acl == "public-read"
msg := sprintf("S3 bucket '%v' must not be publicly readable", [resource.address])
}
This policy fails any Terraform plan that attempts to create a publicly readable S3 bucket — preventing a common cloud misconfiguration before it ever reaches AWS.
Envoy Proxy and Service Mesh Authorization
OPA integrates natively with Envoy proxy via the ext_authz filter, enabling request-level authorization for all traffic passing through the mesh — without any changes to application code.
When a service mesh uses OPA as its external authorization service, every inbound request to every service is evaluated against policy. This enables fine-grained, dynamic authorization rules at the network layer: service A can only call service B's /api/orders endpoint during business hours, or only if the request carries a valid JWT with the required claims.
This is a cornerstone pattern for zero-trust microservices architectures.
CI/CD Pipeline Policy Gates
OPA and Conftest can enforce deployment standards in CI/CD pipelines — acting as policy gates that prevent non-compliant workloads from being deployed to any environment.
Common CI/CD policy checks include:
- Container images must be signed and from approved registries.
- Kubernetes manifests must define resource requests and limits.
- Deployments must not run as root.
- Helm charts must include liveness and readiness probes.
- All resources must carry required metadata labels.
Shifting these checks left into CI/CD pipelines dramatically reduces the number of violations that reach cluster admission control, reducing friction for developers while improving overall compliance posture.
OPA Best Practices for Production Deployments
Use Bundles for Policy Distribution
Never deploy policies as static files in production. Use OPA's bundle mechanism to serve policies from a central bundle server. This enables hot policy updates without restarting OPA instances and provides a single source of truth for policies across all environments.
Enable Decision Logging
OPA can emit a decision log for every policy evaluation — recording the input, the output, and the policy that produced it. Ship these logs to your observability stack (Elasticsearch, Splunk, Datadog) for audit trails, anomaly detection, and policy effectiveness analysis.
decision_logs:
console: true
service: remote_log_service
reporting:
min_delay_seconds: 5
max_delay_seconds: 10
Cache External Data Wisely
OPA evaluates policies synchronously. If your policies depend on external data (user directories, resource inventories), load that data into OPA's data cache via bundles or the push API rather than making synchronous external calls during evaluation. This keeps latency low and evaluation deterministic.
Separate Policy from Data
Keep policies (Rego files) and data (JSON/YAML reference data) in separate bundles with different update frequencies. Policies change less frequently than data (e.g., allowlists, role assignments). Separate bundles allow data to be updated in seconds without triggering a full policy bundle reload.
Lint and Test Every Policy
Use OPA's built-in linter (opa check) and test framework (opa test) in CI/CD. Require 100% test coverage for all policy rules. Review policies like code — with pull requests, peer review, and documented rationale.
Use the OPA Playground for Prototyping
The OPA Playground (play.openpolicyagent.org) is a browser-based environment for writing and testing Rego policies without any local setup. It is invaluable for rapid prototyping, debugging, and sharing policies with colleagues.
OPA vs. Alternatives: Choosing the Right Tool
| Feature | OPA | Kyverno | Cedar | Casbin |
|---|---|---|---|---|
| Language | Rego (declarative) | YAML (K8s-native) | Cedar (structured) | Policy model DSL |
| Kubernetes native | Via Gatekeeper | Native | No | No |
| General purpose | Yes | No (K8s focused) | Limited | Yes |
| Test framework | Built-in | Limited | Limited | No |
| Bundle management | Yes | No | No | No |
| API authorization | Yes | No | Yes | Yes |
| CNCF graduated | Yes | Incubating | No | No |
| Learning curve | Moderate | Low | Low | Low |
OPA is the right choice when you need a general-purpose policy engine that works across Kubernetes, APIs, IaC, and custom applications with a single policy language and a unified decision audit trail.
Kyverno is a strong choice if your policy needs are Kubernetes-only and you want to avoid learning Rego. Many organizations use both: Kyverno for Kubernetes-native admission policies and OPA for cross-stack authorization.
Real-World OPA Use Cases
Netflix uses OPA to authorize API calls across its microservices platform, evaluating millions of policy decisions per second with sub-millisecond latency.
Intuit adopted OPA to enforce consistent cloud governance policies across its multi-cloud, multi-team environment — using bundles to distribute policies from a central governance repository to thousands of OPA instances.
Goldman Sachs uses OPA for Kubernetes admission control and Terraform validation, ensuring that infrastructure deployed across its cloud environments meets strict financial services compliance requirements.
The U.S. Department of Defense uses OPA as part of its Platform One DevSecOps platform, enforcing security policies on Kubernetes workloads across classified and unclassified environments.
Getting Started with OPA: Quick Setup
Install OPA:
# macOS
brew install opa
# Linux
curl -L -o opa https://openpolicyagent.org/downloads/latest/opa_linux_amd64_static
chmod +x opa && sudo mv opa /usr/local/bin/
Write your first policy (policy.rego):
package hello
default allow := false
allow if {
input.user == "alice"
input.action == "read"
}
Evaluate it:
echo '{"user": "alice", "action": "read"}' | opa eval -d policy.rego -I data.hello.allow
# Output: true
echo '{"user": "bob", "action": "write"}' | opa eval -d policy.rego -I data.hello.allow
# Output: false
Run OPA as a server:
opa run --server --addr :8181 policy.rego
Query via REST:
curl -X POST http://localhost:8181/v1/data/hello/allow \
-H 'Content-Type: application/json' \
-d '{"input": {"user": "alice", "action": "read"}}'
# {"result": true}
In under five minutes, you have a running policy engine serving decisions over HTTP — ready to integrate with any application or infrastructure tool in your stack.
Conclusion: OPA as the Policy Backbone of Cloud-Native Infrastructure
Open Policy Agent represents a fundamental shift in how organizations think about policy — from scattered, hard-coded logic to a unified, testable, auditable discipline. By decoupling policy decisions from enforcement, OPA enables teams to govern their entire infrastructure with a single, consistent policy language and a centralized audit trail.
Whether you are enforcing Kubernetes admission policies with Gatekeeper, validating Terraform plans with Conftest, authorizing API calls in a microservices platform, or building a zero-trust service mesh — OPA is the engine that makes it possible at scale.
The investment in learning Rego pays dividends across every layer of your stack. And as your infrastructure grows more complex, having a principled, code-first approach to policy becomes not just a best practice — it becomes a business necessity.
Start with one use case. Write your first Rego policy. Test it. Deploy it. And begin building the policy-as-code foundation your cloud-native infrastructure deserves.

